C++ 设计模式 —— 桥接模式
C++ 设计模式 —— 桥接模式
0. 引用连接
本文主要的思路和代码,来自于对以下连接的学习和实现:
桥接模式
1. 引言
1.1 什么是桥接模式?
- 桥接模式的定义
- 桥接模式的作用
桥接模式,顾名思义,就像是一座连接两岸的桥梁。在软件开发中,我们可以将桥接模式看作是一座连接抽象部分和实现部分的“桥”,通过这座“桥”,我们可以方便地在抽象部分和实现部分之间进行切换,而不需要关心它们之间的实现细节。
桥接模式的核心作用就是降低系统的耦合度,提高扩展性和可维护性。想象一下你正在修建一座连接两个城市的大桥,如果没有这座桥,你需要绕行很远的距离才能到达对岸。同样地,在软件开发中,如果没有桥接模式,抽象部分和实现部分之间的依赖关系可能会变得非常复杂,导致系统难以扩展和维护。
1.2 桥接模式与其他设计模式的关系
- 桥接模式和其他设计模式的关系就像是一组不同的工具,每个工具都有其独特的用途。就像在修建一座桥梁时,我们可以选择使用各种不同的材料和工具来完成任务,而不仅仅是一种。同样地,在软件开发中,我们可以结合使用不同的设计模式来解决不同的问题,而桥接模式通常会于开发前期进行设计。
- 桥接模式、状态模式和策略模式(在某种程度上包括适配器)都具有相似的接口,它们都基于组合模式——即将工作委派给其他对象。但是它们各自解决了不同的问题。例如,如果我们需要将一个类与它的子类进行关联,并且希望在运行时动态地创建子类的实例,那么桥接模式就非常适合。而如果我们需要对一个类的多个行为进行控制,那么状态模式就是一个不错的选择。
- 我们可以将抽象工厂模式和桥接搭配使用。如果由桥接定义的抽象只能与特定实现合作,这一模式搭配就非常有用。在这种情况下,抽象工厂可以对这些关系进行封装,并且对客户端代码隐藏其复杂性。
- 生成器模式和桥接模式也可以很好地结合使用:主管类负责抽象工作,各种不同的生成器负责实现工作。这就好比我们在修建桥梁时,需要不同的工人来完成不同的任务一样,生成器模式可以帮助我们更好地管理代码的生成过程。
桥接模式和其他设计模式都是非常有用的工具,它们可以帮助我们更好地组织和管理代码,提高系统的可维护性和可扩展性。在使用这些工具时,我们需要根据具体的需求和场景来选择合适的模式,以最大程度地发挥它们的效用。
1.3 桥接模式适用的场景
桥接模式的应用场景也非常广泛。比如我们正在开发一个电商网站,其中有一个订单管理系统和一个支付系统。这两个系统分别负责订单的处理和支付的操作,但是它们之间的关系非常紧密。如果没有桥接模式,我们需要在订单管理系统和支付系统中都实现相同的逻辑,这显然是不合理的。而如果我们使用桥接模式,可以将订单管理系统和支付系统分别抽象成两个独立的类,并通过一个桥接器来协调它们之间的交互,这样就可以避免代码冗余和耦合度过高的问题。如果我们概括一下桥接模式的使用场景的话,主要有以下几个:
- 系统中存在多个独立但相互关联的类时,可以使用桥接模式来解耦它们的依赖关系。
- 当需要动态地创建一系列具有相似特性的对象时,可以使用桥接模式来实现对象的创建和初始化过程。
- 当需要在一个系统中灵活地切换不同的算法或策略时,可以使用桥接模式来实现算法或策略的切换和组合。
- 需要通过组合的方式在运行时对算法进行选择时,可以使用桥接模式来避免使用多重条件语句。
- 需要创建一个可以与多种类型接口进行交互的类时,可以使用桥接模式来实现这个抽象。
2. 桥接模式的实现
桥接模式的实现方式就像是一座连接不同平台的桥梁,它通过抽象部分和实现部分之间的关联来实现。如果以连接桥梁为例,其大致的步骤为:
- 需要明确类中独立的维度,也就是确定抽象部分和实现部分之间的独立关系。这些独立的概念可以是抽象/平台、域/基础设施、前端/后端或接口/实现等。
- 要了解客户端的业务需求,并在抽象基类中定义它们。这就像在修建一座桥梁时,我们需要考虑不同的交通需求,比如车辆、行人等,并设计出满足这些需求的桥梁结构。
- 要确定在所有平台上都可执行的业务,并在通用实现接口中声明抽象部分所需的业务。这就像是在选择材料和工具时,我们需要选择那些能够适用于不同平台的通用材料和工具。
- 可以为域内的所有平台创建实现类,但需确保它们遵循实现部分的接口。这就像是在建造桥梁时,我们需要根据设计图纸来建造每个平台的结构。
- 在抽象类中添加指向实现类型的引用成员变量,抽象部分会将大部分工作委派给该成员变量所指向的实现对象。这就像是在建造桥梁时,将具体的施工任务委托给各个工人来完成。
- 如果高层逻辑有多个变体,则可通过扩展抽象基类为每个变体创建一个精确抽象。这就像是在不同的路段上修建不同类型的桥梁,以满足不同的交通需求。
2.1 抽象部分
抽象部分在桥接模式中起到了至关重要的连接作用。它通过定义接口、封装逻辑、委派工作等方式,将不同的平台进行连接,并帮助客户端与实现部分进行交互。
抽象部分就像是一座桥梁的桥墩,它具体负责了下面的一些内容:
- 定义和封装与实现部分相关的接口和逻辑。
- 需要承受来自不同平台的负荷,并将这些负荷传递到实现部分。同样地,抽象部分也需要承载客户端的业务需求,并将这些需求传递给实现部分来完成具体的操作。
- 定义一些通用的方法和属性,这些方法和属性可以被多个实现类共享和使用。这就好比我们在建造一座桥梁时,需要使用一些通用的材料和工具,以便在不同的地方建造出相同的结构。
- 通过添加引用成员变量来委派具体的实现对象。这样,抽象部分就可以将大部分的工作交给具体实现来完成,而自己只需要关注与客户端的交互即可。这就好比我们选择了一个优秀的工人来建造桥梁的一部分,让他来完成整个工作,而自己则专注于监督和管理。
2.2 具体部分
- 定义具体的子类,这些子类实现了抽象部分定义的接口
- 这些子类可以在运行时动态地被创建,并通过抽象部分提供的接口与客户端代码进行交互
具体部分在桥接模式中同样起到了至关重要的作用。它通过实现抽象部分所定义的方法和属性,完成具体的操作,并帮助客户端与实现部分进行交互。同时,具体部分还可以根据抽象部分的定义,使用不同的实现方式来完成不同的业务逻辑。
具体部分就像是一座桥梁的桥面和支撑结构,它具体负责了下面的一些内容:
- 实现抽象部分定义的接口和逻辑。
- 承载客户端的业务需求,并将这些需求实现成具体的操作。
- 通过添加额外的实现类来实现不同的业务逻辑。这就好比我们在建造一座桥梁时,需要使用不同的材料和工具来建造出不同的结构。
- 具体部分也需要根据抽象部分的定义,使用不同的实现方式来完成不同的业务逻辑。
- 通过扩展抽象基类为每个变体创建一个精确抽象。这样,我们就可以针对不同的平台创建不同的具体实现,以满足不同的业务需求。这就好比我们在不同的路段上修建不同类型的桥梁,以满足不同的交通需求。
3. 桥接模式的优点和缺点
3.1 优点
- 允许我们创建与平台无关的类和程序。这意味着我们可以将不同的平台抽象出来,形成一个统一的高层接口。这样,无论我们在哪个平台上进行开发,都可以使用相同的类和接口来进行交互,而不需要关心具体的平台细节。
- 客户端代码仅与高层抽象部分进行互动,不会接触到平台的详细信息。这就好比我们通过一座桥梁来连接两个不同的地点,我们只需要关注桥梁上的路径和交通情况,而不需要了解每个地点的具体信息。
- 桥接模式还遵循了开闭原则。我们可以新增抽象部分和实现部分,并且它们之间不会相互影响。这就好比我们可以通过添加新的桥梁来连接更多的地点,而不需要修改已有的桥梁结构。这种灵活性使得我们的系统可以更容易地适应新的需求和变化。
- 桥接模式也符合单一职责原则。抽象部分专注于处理高层逻辑,实现部分处理平台细节。这就好比一个桥梁的设计者只需要负责设计出合适的桥梁结构和路径规划,而不需要关心具体的材料和施工细节。这种分离使得各个部分的职责更加明确,易于维护和扩展。
综上所述,桥接模式的优点在于它可以帮助我们更好地组织和管理代码,提高系统的可维护性和可扩展性。通过创建与平台无关的类和程序、客户端代码仅与高层抽象部分进行互动、遵循开闭原则以及单一职责原则等特性,桥接模式为我们提供了一个灵活、高效且易于维护的设计方案。
3.2 缺点
- 对于高内聚的类来说,使用桥接模式可能会让代码变得更加复杂。这是因为在桥接模式中,我们需要将抽象部分和实现部分分离开来,这可能会增加代码的复杂度。就像建造一座复杂的桥梁需要更多的设计和施工工作一样,使用桥接模式也需要更多的思考和编码工作。
- 使用桥接模式可能会导致抽象部分的代码量增加,从而影响系统的性能。这是因为抽象部分需要承担更多的职责和功能,可能需要处理更多的细节和逻辑。就像一座桥梁上的支撑结构需要承受更大的负荷一样,抽象部分也需要处理更多的业务逻辑和操作。
- 使用桥接模式可能会增加系统的复杂性,使得理解和修改代码变得更加困难。这是因为桥接模式将抽象部分和实现部分分离开来,可能会增加代码的耦合度和依赖关系。就像建造一座复杂的桥梁需要更多的协调和管理一样,使用桥接模式也需要更多的关注和维护。
- 客户端代码不正确地使用了抽象部分提供的接口,可能会导致系统出现错误或异常行为。这是因为抽象部分需要负责定义和封装接口,如果客户端代码没有正确地调用这些接口,就会导致系统的错误和异常。就像使用错误的桥梁路径会导致交通拥堵一样,客户端代码的错误使用也会给系统带来问题。
综上所述,桥接模式是一种灵活的设计模式,可以帮助我们更好地组织和管理代码。然而,在使用该模式时也需要注意一些潜在的问题和挑战。就像建造一座桥梁需要考虑许多因素一样,使用桥接模式也需要综合考虑各种因素,以确保系统的正确性和可靠性。
4. 代码实现
4.1 真实案例伪代码描述
// “抽象部分”定义了两个类层次结构中“控制”部分的接口。它管理着一个指向“实
// 现部分”层次结构中对象的引用,并会将所有真实工作委派给该对象。
class RemoteControl isprotected field device: Deviceconstructor RemoteControl(device: Device) isthis.device = devicemethod togglePower() isif (device.isEnabled()) thendevice.disable()elsedevice.enable()method volumeDown() isdevice.setVolume(device.getVolume() - 10)method volumeUp() isdevice.setVolume(device.getVolume() + 10)method channelDown() isdevice.setChannel(device.getChannel() - 1)method channelUp() isdevice.setChannel(device.getChannel() + 1)// 你可以独立于设备类的方式从抽象层中扩展类。
class AdvancedRemoteControl extends RemoteControl ismethod mute() isdevice.setVolume(0)// “实现部分”接口声明了在所有具体实现类中通用的方法。它不需要与抽象接口相
// 匹配。实际上,这两个接口可以完全不一样。通常实现接口只提供原语操作,而
// 抽象接口则会基于这些操作定义较高层次的操作。
interface Device ismethod isEnabled()method enable()method disable()method getVolume()method setVolume(percent)method getChannel()method setChannel(channel)// 所有设备都遵循相同的接口。
class Tv implements Device is// ……class Radio implements Device is// ……// 客户端代码中的某个位置。
tv = new Tv()
remote = new RemoteControl(tv)
remote.togglePower()radio = new Radio()
remote = new AdvancedRemoteControl(radio)
4.2 概念示例代码
#include <iostream>
#include <string>/*** The Implementation defines the interface for all implementation classes. It* doesn't have to match the Abstraction's interface. In fact, the two* interfaces can be entirely different. Typically the Implementation interface* provides only primitive operations, while the Abstraction defines higher-* level operations based on those primitives.*/
class Implementation
{
public:virtual ~Implementation() {}virtual std::string OperationImplementation() const = 0;
};/*** Each Concrete Implementation corresponds to a specific platform and* implements the Implementation interface using that platform's API.*/
class ConcreteImplementationA : public Implementation
{
public:std::string OperationImplementation() const override{return "ConcreteImplementationA: Here's the result on the platform A.\n";}
};
class ConcreteImplementationB : public Implementation
{
public:std::string OperationImplementation() const override{return "ConcreteImplementationB: Here's the result on the platform B.\n";}
};/*** The Abstraction defines the interface for the "control" part of the two class* hierarchies. It maintains a reference to an object of the Implementation* hierarchy and delegates all of the real work to this object.*/
class Abstraction
{/*** @var Implementation*/
protected:Implementation *implementation_;public:Abstraction(Implementation *implementation) : implementation_(implementation){}virtual ~Abstraction(){}virtual std::string Operation() const{return "Abstraction: Base operation with:\n" +this->implementation_->OperationImplementation();}
};/*** You can extend the Abstraction without changing the Implementation classes.*/
class ExtendedAbstraction : public Abstraction
{
public:ExtendedAbstraction(Implementation *implementation) : Abstraction(implementation){}std::string Operation() const override{return "ExtendedAbstraction: Extended operation with:\n" +this->implementation_->OperationImplementation();}
};/*** Except for the initialization phase, where an Abstraction object gets linked* with a specific Implementation object, the client code should only depend on* the Abstraction class. This way the client code can support any abstraction-* implementation combination.*/
void ClientCode(const Abstraction &abstraction)
{// ...std::cout << abstraction.Operation();// ...
}/*** The client code should be able to work with any pre-configured abstraction-* implementation combination.*/
void BridgeExample()
{Implementation *implementation = new ConcreteImplementationA;Abstraction *abstraction = new Abstraction(implementation);ClientCode(*abstraction);std::cout << std::endl;delete implementation;delete abstraction;implementation = new ConcreteImplementationB;abstraction = new ExtendedAbstraction(implementation);ClientCode(*abstraction);delete implementation;delete abstraction;
}
4.3 真实案例代码
4.3.1 remote_exmaple.h
#ifndef _REMOTE_EXAMPLE_H_
#define _REMOTE_EXAMPLE_H_// The abstract Device class.
class Device
{
public:virtual bool isEnabled() = 0;virtual void enable() = 0;virtual void disable() = 0;virtual int getVolume() = 0;virtual void setVolume(int volume) = 0;virtual int getChannel() = 0;virtual void setChannel(int channel) = 0;protected:virtual bool checkIsValidVolume(int volume) const = 0;virtual bool checkIsValidChannel(int channel) const = 0;
};class RemoteControl
{
protected:Device* m_pDevice = nullptr;
public:RemoteControl(Device* pDevice);void togglePower();void volumeDown();void volumeUp();void channelDown();void channelUp();
};class AdvancedRemoteControl : public RemoteControl
{
public:void mute();
};class TVDevice : public Device
{
public:TVDevice(bool isEnabled = false, int volume = 1, int channel = 0);bool isEnabled() override;void enable() override;void disable() override;int getVolume() override;void setVolume(int volume) override;int getChannel() override;void setChannel(int channel) override;void printDeviceInfo();private:bool checkIsValidVolume(int volume) const override;bool checkIsValidChannel(int channel) const override;private:bool m_isEnabled = false;int m_volume = 1;int m_channel = 0;
};class RadioDevice : public Device
{
public:RadioDevice(bool isEnabled = false, int volume = 1, int channel = 0);bool isEnabled() override;void enable() override;void disable() override;int getVolume() override;void setVolume(int volume) override;int getChannel() override;void setChannel(int channel) override;void printDeviceInfo();private:bool checkIsValidVolume(int volume) const override;bool checkIsValidChannel(int channel) const override;private:bool m_isEnabled = false;int m_volume = 1;int m_channel = 0;
};class RemoteExample
{
public:static void TVControlCase();static void RadioControlCase();
};#endif // _REMOTE_EXAMPLE_H_
4.3.2 remote_example.cpp
#include "remote_example.h"#include <iostream>namespace
{constexpr int TV_VOLUME_MAX = 100;constexpr int TV_CHANNEL_MAX = 256;constexpr int RADIO_VOLUME_MAX = 200;constexpr int RADIO_CHANNEL_MAX = 512;
}RemoteControl::RemoteControl(Device* pDevice): m_pDevice(pDevice)
{
}void RemoteControl::togglePower()
{if (m_pDevice->isEnabled()) {m_pDevice->disable();} else {m_pDevice->enable();}
}void RemoteControl::volumeDown()
{m_pDevice->setVolume(m_pDevice->getVolume() - 10);
}void RemoteControl::volumeUp()
{m_pDevice->setVolume(m_pDevice->getVolume() + 10);
}void RemoteControl::channelDown()
{m_pDevice->setChannel(m_pDevice->getChannel() - 1);
}void RemoteControl::channelUp()
{m_pDevice->setChannel(m_pDevice->getChannel() + 1);
}void AdvancedRemoteControl::mute()
{m_pDevice->setVolume(0);
}TVDevice::TVDevice(bool isEnabled /*= true*/, int volume /*= 1*/, int channel /*= 0*/): m_isEnabled(isEnabled), m_volume(volume), m_channel(channel)
{
}bool TVDevice::isEnabled()
{return m_isEnabled;
}void TVDevice::enable()
{m_isEnabled = true;
}void TVDevice::disable()
{m_isEnabled = false;
}int TVDevice::getVolume()
{return m_volume;
}void TVDevice::setVolume(int volume)
{if (checkIsValidVolume(volume)){m_volume = volume;}else{std::cout << "Error volume for this TV Device!" << std::endl;}
}int TVDevice::getChannel()
{return m_channel;
}void TVDevice::setChannel(int channel)
{if (checkIsValidChannel(channel)){m_channel = channel;}else{std::cout << "Error channel for this TV Device!" << std::endl;}
}void TVDevice::printDeviceInfo()
{std::cout << "TVDevice Info : Volume :"<< m_volume << '\t' << "Channel : " << m_channel << std::endl;
}bool TVDevice::checkIsValidVolume(int volume) const
{return (volume >= 0 && volume <= TV_VOLUME_MAX) ? true : false;
}bool TVDevice::checkIsValidChannel(int channel) const
{return (channel >= 0 && channel <= TV_CHANNEL_MAX) ? true : false;
}RadioDevice::RadioDevice(bool isEnabled /*= true*/, int volume /*= 1*/, int channel /*= 0*/): m_isEnabled(isEnabled), m_volume(volume), m_channel(channel)
{
}bool RadioDevice::isEnabled()
{return m_isEnabled;
}void RadioDevice::enable()
{m_isEnabled = true;
}void RadioDevice::disable()
{m_isEnabled = false;
}int RadioDevice::getVolume()
{return m_volume;
}void RadioDevice::setVolume(int volume)
{if (checkIsValidVolume(volume)){m_volume = volume;}else{std::cout << "Error volume for this TV Device!" << std::endl;}
}int RadioDevice::getChannel()
{return m_channel;
}void RadioDevice::setChannel(int channel)
{if (checkIsValidChannel(channel)){m_channel = channel;}else{std::cout << "Error channel for this TV Device!" << std::endl;}
}bool RadioDevice::checkIsValidVolume(int volume) const
{return (volume >= 0 && volume <= RADIO_VOLUME_MAX) ? true : false;
}bool RadioDevice::checkIsValidChannel(int channel) const
{return (channel >= 0 && channel <= RADIO_CHANNEL_MAX) ? true : false;
}void RadioDevice::printDeviceInfo()
{std::cout << "RadioDevice Info : Volume :"<< m_volume << '\t' << "Channel : " << m_channel << std::endl;
}void RemoteExample::TVControlCase()
{std::cout << "\nTV Device example case start." << std::endl;TVDevice tv;RemoteControl remoteTv(&tv);remoteTv.togglePower();tv.printDeviceInfo();remoteTv.channelUp();std::cout << "After TV channel up:" << std::endl;tv.printDeviceInfo();remoteTv.volumeUp();std::cout << "After TV volume Up:" << std::endl;tv.printDeviceInfo();AdvancedRemoteControl adRemote(&tv);adRemote.mute();std::cout << "After TV is muted:" << std::endl;tv.printDeviceInfo();std::cout << "\nTV Device example case end." << std::endl;
}void RemoteExample::RadioControlCase()
{std::cout << "\nRadio Device example case start." << std::endl;RadioDevice radio;RemoteControl remoteRadio(&radio);remoteRadio.togglePower();radio.printDeviceInfo();remoteRadio.channelUp();std::cout << "After Radio channel up:" << std::endl;radio.printDeviceInfo();remoteRadio.volumeUp();std::cout << "After Radio volume Up:" << std::endl;radio.printDeviceInfo();AdvancedRemoteControl adRemote(&radio);adRemote.mute();std::cout << "After Radio is muted:" << std::endl;radio.printDeviceInfo();std::cout << "\nRadio Device example case end." << std::endl;
}
4.4 代码分析
4.4.1 概念代码分析
相关代码由以下类组成:
-
Implementation:这是一个抽象基类,定义了所有实现类的接口。它有一个纯虚函数OperationImplementation()。 -
ConcreteImplementationA和ConcreteImplementationB:这两个类是Implementation的具体实现,分别对应平台A和平台B。它们实现了OperationImplementation()方法,返回特定平台的结果。 -
Abstraction:这个类定义了控制部分的接口,并维护了一个对Implementation类的对象的引用。它有一个虚函数Operation(),该函数通过委托给implementation_对象来执行实际工作。 -
ExtendedAbstraction:这是Abstraction的子类,扩展了其功能。它在Operation()中添加了一些额外的操作。 -
ClientCode和BridgeExample:这两个函数/方法用于客户端代码,与抽象和实现类进行交互。
这些类的角色如下:
Implementation:定义了所有实现类的接口。ConcreteImplementationA和ConcreteImplementationB:实现了Implementation接口,提供了特定平台的实现。Abstraction:作为控制部分的接口,封装了对Implementation对象的引用,并提供了基本的操作。ExtendedAbstraction:扩展了Abstraction的功能。ClientCode和BridgeExample:客户端代码,与抽象和实现类进行交互。
模式中的各个元素以以下方式相互关联:
Abstraction类包含一个指向Implementation类的指针,并通过该指针调用OperationImplementation()方法。ExtendedAbstraction类继承自Abstraction类,并重写了Operation()方法,增加了额外的操作。ClientCode函数接受一个Abstraction类的引用,并通过该引用调用Operation()方法。BridgeExample函数演示了如何使用不同的Abstraction和Implementation组合创建对象,并通过ClientCode函数调用相应的操作。
4.4.2 真实案例代码分析
相关代码由以下类组成:
-
Device:这是一个抽象类,定义了一些设备共有的属性和方法,如检查设备是否启用、获取和设置音量和频道等。 -
RemoteControl:这是一个基类,它有一个指向Device类的指针,并定义了一些远程控制设备的基本操作,如切换电源、调整音量和频道等。 -
AdvancedRemoteControl:这是RemoteControl的子类,增加了静音功能。 -
TVDevice和RadioDevice:这两个类都继承自Device类,分别代表电视设备和无线电设备。它们都实现了Device类中的虚函数,并添加了一些特定于设备的属性和方法,如打印设备信息等。 -
RemoteExample:这是一个包含两个静态方法的类,用于在其他地方调用以执行特定的远程控制案例。
这些类的角色如下:
Device:作为所有设备的基类,定义了通用的设备属性和方法。RemoteControl:作为远程控制的基类,提供了一些设备的基本操作。AdvancedRemoteControl:扩展了远程控制的功能,包括静音。TVDevice和RadioDevice:这两个类代表了具体的设备类型,它们继承了Device类,并实现了Device类中的方法,添加了特定于设备的属性和方法。RemoteExample:包含了两个静态方法,用于在其他地方执行特定的远程控制案例。
相关文章:
C++ 设计模式 —— 桥接模式
C 设计模式 —— 桥接模式 0. 引用连接 本文主要的思路和代码,来自于对以下连接的学习和实现: 桥接模式 1. 引言 1.1 什么是桥接模式? 桥接模式的定义桥接模式的作用 桥接模式,顾名思义,就像是一座连接两岸的桥…...
微信怎么删除好友?非常简单,2个方法!
随着生活和工作的节奏加快,这也导致我们微信里的联系人变得越来越多。有时候,我们可能只是需要给对方转钱、发送照片或者是一些其他理由。 而这部分“好友”可能除了这次交流后再也没有别的联系了,那么这时候大家可能会想把他们删除。那么微…...
小谈设计模式(25)—职责链模式
小谈设计模式(25)—职责链模式 专栏介绍专栏地址专栏介绍 职责链模式分析角色分析抽象处理者(Handler)具体处理者(ConcreteHandler)客户端(Client) 优缺点分析优点123 缺点12 应用场…...
Python- JSON-RPC创建一个远程过程调用
我们使用JSON-RPC创建一个远程过程调用的例子,我们将使用jsonrpcserver库和Flask框架创建一个后端服务,并使用jsonrpcclient作为客户端。这个例子将包括: 一个计算服务,提供加、减、乘、除四个方法。错误处理:除数为零…...
Linux中scp命令复制文件
scp命令是在Linux中用于在本地主机和远程主机之间进行安全传输文件的命令。下面是使用scp命令的语法: scp [参数] [来源路径] [目标路径]参数: -r:递归复制整个目录。-P:指定远程主机的端口。-p:保留原文件的修改时间…...
Interlay采用Moonbeam路由流动性,为波卡发展更多流动性
波卡上的首选多链开发平台Moonbeam宣布Interlay现在支持由Carrier赋能的Moonbeam路由流动性。Carrier是一个功能强大的token和NFT跨链桥,支持超过12个网络。Interlay是波卡上的一条平行链,与HydraDX一起通过Wormhole、Moonbeam和Carrier为波卡生态挖掘流…...
Jetson Orin NX 开发指南(9): Pixhawk 6X 飞控固件的烧写与 QGroundControl 参数配置
一、前言 由于 Jetson Orin NX 常被用作自主无人机机载电脑,其往往需要与烧写了 PX4 固件的飞控进行通信,飞控的烧写与配置往往会遇到很多问题,因此本文将介绍时下最款的 Pixhawk 系列飞控 Pixhawk 6X,做一个固件烧写和参数配置的…...
Redis(四)多级缓存
文章目录 一、传统缓存存在的问题二、多级缓存方案三、JVM进程缓存案例演示: 四、Lua语法入门Lua语言入门 五、多级缓存(一)安装OpenResty(二)OpenResty入门(三)请求参数处理(四&…...
网站安全防护
1.确保环境安全例如近期LNMP投毒事件 2.nginx/php隐藏版本号/关闭报错显示行 3.限制3306端口/phpMyAdmin尽量不用 4.修改指纹例如X-Powered-By/Cookie 5.上线前后进行主机漏扫网站漏扫 6.系统安装杀毒ClamAV 7.更改后台管理入口 8.安装雷池WAF防护 9.网站使用https协议 10.后台…...
腾讯云南京地域怎么样?南京服务器IP测速Ping值延迟
腾讯云服务器南京地域怎么样?南京地域很不错,正好处于中间的位置,南方北方用户均可以选择,网络延迟更低速度更快,并且目前南京地域有活动,南京地域可用区可选南京一区、南京二区和南京三区,腾讯…...
Harbor 简介
1)什么是Harbor Harbor 是 VMware 公司开源的企业级 Docker Registry 项目,其目标是帮助用户迅速搭建一个企业级的 Docker Registry 服务。 Harbor以 Docker 公司开源的 Registry 为基础,提供了图形管理 UI 、基于角色的访问控制(Role Based…...
RuntimeError: “LayerNormKernelImpl“ not implemented for ‘Half‘解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...
《向量数据库指南》——向量数据库与 ANN 算法库的区别
向量数据库与 ANN 算法库的区别 我们经常听到一个这样的错误观念——向量数据库只是在 ANN(approximate nearest neighbor,近似最近邻)算法上封装了一层。但这种说法大错特错。 向量数据库可以处理大规模数据,而 ANN 算法库只能处理小型的数据集 从本质上来看,以 Milvus 为…...
JavaScript-es6-新版语法-export-import
文章目录 1.export2.import3.export default 命令4.总结 在JavaScript ES6中,export与export default均可用于导出常量、函数、文件、模块等。模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输…...
[elasticsearch]使用postman来查询数据
最近需要debug程序,debug的时候需要查找elasticsearch里面的数据是否正确。 第一步建立一个post请求,并按照图下的方式填上ur和参数: 发送post请求,url为: http://ip:port/index_name/_search我这里查询的是title字…...
【小程序练习】文件操作案例
文章目录 前言1. 案例12. 案例23. 案例3总结 前言 我们学会了文件的基本操作 文件内容读写操作,接下来,我们实现一些小工具程序,来锻炼我们的能力。 关注收藏, 开始学习吧🧐 1. 案例1 扫描指定目录,并找到名称中包…...
flask框架-[实现websocket]:将socketio处理函数部分集中管理,使用类的方式来管理,集中管理socketio处理函数
一、项目依赖 APScheduler3.10.4 eventlet0.33.3 Flask2.1.3 Flask-Caching1.10.1 Flask-Cors3.0.10 Flask-Migrate2.7.0 Flask-RESTful0.3.9 Flask-SocketIO5.1.1 Flask-SQLAlchemy2.5.1 PyJWT2.3.0 PyMySQL1.0.2 redis3.5.3 SQLAlchemy1.4.0 #额外修改 Werkzeug2.0.2 #额外修…...
Vue的学习补充
1.Vue路由-404 作用:当路径找不到匹配时,给个提示页面 位置:配在路由最后 语法:path:*(任意路径)-前面不匹配就命中最后这个 2.Vue路由-模式设置 hash路由(默认) 例如ÿ…...
vue移动端H5调起手机发送短信(兼容ios和android)
移动端h6页面调起手机发送短信功能,ios和android的兼容写法不一样。 android window.location.href sms:10086?body${encodeURIComponent(Hello, 测试短信发送)} ios window.location.href sms:10086&body${encodeURIComponent(Hello, 测试短信发送)}//或者w…...
spring boot RabbitMq基础教程
RabbitMq 由于RabbitMQ采用了AMQP协议,因此它具备跨语言的特性。任何语言只要遵循AMQP协议收发消息,都可以与RabbitMQ交互。并且RabbitMQ官方也提供了各种不同语言的客户端。 但是,RabbitMQ官方提供的Java客户端编码相对复杂,一般…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...
