常用的二十种设计模式(下)-C++
设计模式
C++中常用的设计模式有很多,设计模式是解决常见问题的经过验证的最佳实践。以下是一些常用的设计模式:
- 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。
- 工厂模式(Factory):用于创建对象,而不需要指定具体类。
- 抽象工厂模式(Abstract Factory):提供一组相关或相互依赖的对象,而不指定其具体类。
- 建造者模式(Builder):将一个复杂对象的构建与其表示分离,使相同的构建过程可以创建不同的表示。
- 原型模式(Prototype):通过克隆已有对象来创建新对象,以避免直接使用构造函数。
- 适配器模式(Adapter):将一个接口转换成另一个客户希望使用的接口。
- 装饰器模式(Decorator):动态地给一个对象添加一些额外的职责,而不需要子类化。
- 代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问。
- 观察者模式(Observer):定义了一种一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知。
- 策略模式(Strategy):定义一系列算法,封装它们,使它们可以相互替换。
- 命令模式(Command):将一个请求封装成一个对象,从而允许使用不同的请求、队列或日志请求。
- 状态模式(State):允许对象在其内部状态改变时改变其行为。
- 模板方法模式(Template Method):定义算法的骨架,允许子类重写其中的步骤。
- 访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
- 组合模式(Composite):将对象组合成树形结构以表示“部分-整体”的层次结构。
- 迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
- 中介者模式(Mediator):定义一个对象,封装一系列对象之间的交互,从而降低对象之间的耦合度。
- 备忘录模式(Memento):不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。
- 桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们可以独立变化。
- 解释器模式(Interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
这些设计模式都有特定的应用场景,可以根据问题的性质选择合适的设计模式来解决。设计模式有助于提高代码的可维护性、可扩展性和复用性。
11.命令模式
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而允许你参数化不同的请求、将请求排队、记录请求日志,或者支持可撤销的操作。命令模式的核心思想是将请求的发送者与请求的接收者解耦。
命令模式包含以下几个主要角色:
- 命令(Command):命令是一个抽象类或接口,它定义了执行请求的方法
execute
。具体的命令类将实现这个接口,将请求参数绑定到接收者,并在execute
方法中执行具体的操作。 - 具体命令(Concrete Command):具体命令是命令接口的具体实现,它包括一个接收者对象,负责执行具体的操作。
- 接收者(Receiver):接收者是具体命令类的执行者,它包含了具体的业务逻辑,命令类将请求委派给接收者来执行。
- 调用者(Invoker):调用者是负责向命令对象发送请求的对象,它不需要了解命令是如何执行的,只需要将请求发送给命令对象。
命令模式的主要优点包括:
- 解耦命令发送者和命令执行者。
- 支持撤销和重做操作。
- 支持命令的组合,可以构建复杂的命令序列。
以下是一个简单的命令模式的 C++ 示例:
#include <iostream>// 命令接口
class Command {
public:virtual void execute() = 0;
};// 具体命令
class LightOnCommand : public Command {
public:LightOnCommand(Light& light) : light_(light) {}void execute() override {light_.on();}private:Light& light_;
};// 接收者
class Light {
public:void on() {std::cout << "Light is on" << std::endl;}void off() {std::cout << "Light is off" << std::endl;}
};// 调用者
class RemoteControl {
public:void setCommand(Command* command) {command_ = command;}void pressButton() {command_->execute();}private:Command* command_;
};int main() {Light livingRoomLight;LightOnCommand livingRoomLightOn(livingRoomLight);RemoteControl remote;remote.setCommand(&livingRoomLightOn);remote.pressButton();return 0;
}
在这个示例中,我们有一个命令接口 Command
,定义了 execute
方法。具体命令 LightOnCommand
包含一个接收者对象 Light
,在 execute
方法中调用接收者的方法。
调用者 RemoteControl
通过设置具体命令对象,并调用 pressButton
方法来执行命令。这种方式实现了调用者和接收者的解耦,调用者只需要知道如何发送命令,而不需要知道命令是如何执行的。
命令模式非常适用于构建可扩展和可维护的系统,特别是需要支持撤销、重做和构建复杂命令序列的情况。
12.状态模式
状态模式(State Pattern)是一种行为型设计模式,它允许对象在其内部状态改变时改变其行为。状态模式的核心思想是将不同状态抽象为独立的状态类,使对象能够在运行时切换不同状态,而不需要修改其代码。
状态模式包含以下几个主要角色:
- 上下文(Context):上下文类是包含状态的对象,它在运行时可以切换不同的状态对象。上下文类通常包括状态接口的引用,用于与当前状态进行交互。
- 状态(State):状态是一个抽象类或接口,它定义了一个特定状态的行为。具体的状态类将实现这个接口,每个状态类代表一个具体的状态,并定义了状态下的行为。
状态模式的主要优点包括:
- 将状态的行为封装在独立的状态类中,降低了上下文类的复杂性。
- 支持动态切换状态,使对象能够在运行时改变行为。
- 易于添加新的状态,同时不需要修改现有代码。
以下是一个简单的状态模式的 C++ 示例:
#include <iostream>// 状态接口
class State {
public:virtual void handle() = 0;
};// 具体状态 A
class ConcreteStateA : public State {
public:void handle() override {std::cout << "Handling state A" << std::endl;}
};// 具体状态 B
class ConcreteStateB : public State {
public:void handle() override {std::cout << "Handling state B" << std::endl;}
};// 上下文
class Context {
public:void setState(State* state) {state_ = state;}void request() {state_->handle();}private:State* state_;
};int main() {ConcreteStateA stateA;ConcreteStateB stateB;Context context;context.setState(&stateA);context.request();context.setState(&stateB);context.request();return 0;
}
在这个示例中,我们有一个状态接口 State
,定义了 handle
方法,具体状态类 ConcreteStateA
和 ConcreteStateB
分别实现了这个接口。
上下的 Context
包含一个状态对象的引用,它可以在运行时切换不同的状态。当调用 request
方法时,上下文对象会委托给当前状态对象来执行特定的行为。
状态模式使对象能够根据内部状态的改变而改变行为,这有助于消除大量的条件分支,提高了代码的可维护性和可扩展性。状态模式常用于处理对象的状态机、有限状态机、工作流等场景。
13.模板方法模式
模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一组算法的骨架,将一些步骤延迟到子类实现。模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。
模板方法模式包含以下几个主要角色:
- 模板方法(Template Method):模板方法是一个抽象类,它定义了一个算法的骨架,包括一组步骤,其中一些步骤由子类实现。
- 具体模板(Concrete Template):具体模板类是模板方法的具体实现,它实现了模板方法中定义的具体步骤,其中一些步骤可能由子类实现。
模板方法模式的主要特点包括:
- 定义了一个算法的骨架,其中一些步骤延迟到子类实现。
- 子类可以重新定义模板方法中的特定步骤,以满足其特定需求,而无需改变算法的整体结构。
- 模板方法模式使得代码重用和扩展变得更加容易。
以下是一个简单的模板方法模式的 C++ 示例:
#include <iostream>// 模板方法
class AbstractClass {
public:// 模板方法定义了算法的骨架,包括一组步骤void templateMethod() {step1();step2();step3();}// 具体步骤由子类实现virtual void step1() = 0;virtual void step2() = 0;virtual void step3() = 0;
};// 具体模板
class ConcreteClassA : public AbstractClass {
public:void step1() override {std::cout << "ConcreteClassA - Step 1" << std::endl;}void step2() override {std::cout << "ConcreteClassA - Step 2" << std::endl;}void step3() override {std::cout << "ConcreteClassA - Step 3" << std::endl;}
};class ConcreteClassB : public AbstractClass {
public:void step1() override {std::cout << "ConcreteClassB - Step 1" << std::endl;}void step2() override {std::cout << "ConcreteClassB - Step 2" << std::endl;}void step3() override {std::cout << "ConcreteClassB - Step 3" << std::endl;}
};int main() {ConcreteClassA a;ConcreteClassB b;a.templateMethod();b.templateMethod();return 0;
}
在这个示例中,我们有一个抽象类 AbstractClass
,它定义了一个模板方法 templateMethod
,包括一组步骤(step1
、step2
、step3
)。具体的步骤由子类实现。具体子类 ConcreteClassA
和 ConcreteClassB
分别实现了这些步骤。
当我们调用 templateMethod
时,模板方法会按照定义的算法骨架执行各个步骤。不同的子类可以重新定义这些步骤,以满足其特定需求。
模板方法模式非常适用于需要定义算法骨架但允许某些步骤由子类自定义的情况,例如,创建框架、工作流程或其他多步骤的操作。这有助于提高代码的可复用性和可维护性。
14.访问者模式(Visitor)
访问者模式(Visitor Pattern)是一种行为型设计模式,它用于分离数据结构和数据操作,并允许在不修改数据结构的情况下添加新的操作。访问者模式的核心思想是将数据结构和操作分离,使得数据结构可以在不同的操作下表现不同的行为。
访问者模式包含以下几个主要角色:
- 访问者(Visitor):访问者是一个抽象类或接口,它定义了一组访问操作,每个操作对应一个具体的元素类型。
- 具体访问者(Concrete Visitor):具体访问者是访问者接口的具体实现,每个具体访问者实现了一组具体的访问操作。
- 元素(Element):元素是一个抽象类或接口,它定义了一个
accept
方法,该方法接受一个访问者对象作为参数,允许访问者访问该元素。 - 具体元素(Concrete Element):具体元素是元素接口的具体实现,每个具体元素实现了
accept
方法,用于调用访问者的相应操作。 - 对象结构(Object Structure):对象结构是一个包含多个元素的集合,它通常提供一个接受访问者的方法,用于遍历元素并调用访问者的操作。
访问者模式的主要优点包括:
- 分离了数据结构和数据操作,使得可以添加新的操作而无需修改现有数据结构。
- 支持对数据结构进行不同的操作,从而提供更多的灵活性和扩展性。
- 使得数据结构可以遵循开闭原则(Open/Closed Principle),即对扩展开放,对修改关闭。
以下是一个简单的访问者模式的 C++ 示例:
#include <iostream>
#include <vector>// 前向声明,让元素类知道访问者类
class Visitor;// 元素接口
class Element {
public:virtual void accept(Visitor& visitor) = 0;
};// 具体元素 A
class ConcreteElementA : public Element {
public:void accept(Visitor& visitor) override {visitor.visitElementA(*this);}void operationA() {std::cout << "Operation A on ConcreteElementA" << std::endl;}
};// 具体元素 B
class ConcreteElementB : public Element {
public:void accept(Visitor& visitor) override {visitor.visitElementB(*this);}void operationB() {std::cout << "Operation B on ConcreteElementB" << std::endl;}
};// 访问者接口
class Visitor {
public:virtual void visitElementA(ConcreteElementA& element) = 0;virtual void visitElementB(ConcreteElementB& element) = 0;
};// 具体访问者
class ConcreteVisitor : public Visitor {
public:void visitElementA(ConcreteElementA& element) override {element.operationA();}void visitElementB(ConcreteElementB& element) override {element.operationB();}
};int main() {ConcreteElementA elementA;ConcreteElementB elementB;ConcreteVisitor visitor;elementA.accept(visitor);elementB.accept(visitor);return 0;
}
在这个示例中,我们有两种具体元素 ConcreteElementA
和 ConcreteElementB
,它们都实现了元素接口,并提供了 accept
方法,以允许访问者访问这些元素。
具体访问者 ConcreteVisitor
实现了访问者接口中定义的访问操作,其中包括操作 A 和操作 B。
通过访问者模式,我们可以在不修改元素类的情况下,为元素类添加新的操作(在这个示例中是操作 A 和操作 B)。这种分离数据结构和操作的设计模式使得代码更加灵活和可维护。
15.组合模式(Composite)
组合模式(Composite Pattern)是一种结构型设计模式,用于将对象组合成树状结构以表示“部分-整体”的层次结构。组合模式允许客户端以一致的方式处理单个对象和对象组合。
组合模式包含以下几个主要角色:
- 组件(Component):组件是一个抽象类或接口,它声明了管理子组件的方法,以及其他操作的接口。
- 叶子(Leaf):叶子是组件的具体实现,它表示树结构中的叶子节点,不包含子组件。
- 容器(Composite):容器是组件的具体实现,它表示树结构中的分支节点,可以包含子组件。容器类通常会实现管理子组件的方法。
组合模式的主要优点包括:
- 客户端可以以一致的方式处理单个对象和对象组合,无需知道具体组件的类型。
- 可以递归地构建复杂的对象结构,使代码具有更高的可扩展性。
- 通过组合模式,可以更容易地添加新的组件类型。
以下是一个简单的组合模式的 C++ 示例:
#include <iostream>
#include <vector>// 抽象组件
class Component {
public:virtual void operation() = 0;
};// 叶子组件
class Leaf : public Component {
public:void operation() override {std::cout << "Leaf operation" << std::endl;}
};// 容器组件
class Composite : public Component {
public:void operation() override {std::cout << "Composite operation" << std::endl;// 调用子组件的操作for (Component* child : children_) {child->operation();}}void add(Component* component) {children_.push_back(component);}private:std::vector<Component*> children_;
};int main() {Leaf leaf1, leaf2;Composite composite;composite.add(&leaf1);composite.add(&leaf2);composite.operation();return 0;
}
在这个示例中,我们有一个抽象组件 Component
,包括一个操作方法 operation
。Leaf
类是叶子组件,它是组件的具体实现,而 Composite
类是容器组件,它可以包含多个子组件。
在 main
函数中,我们创建了两个叶子组件和一个容器组件,并将叶子组件添加到容器组件中。当调用容器组件的 operation
方法时,它会递归调用其子组件的 operation
方法,从而实现了整体-部分的层次结构。
组合模式常用于处理树状结构,例如文件系统、图形界面控件、组织架构等情况,其中可以递归地组合和管理各种对象。
16.迭代器模式
迭代器模式(Iterator Pattern)是一种行为型设计模式,用于提供一种访问聚合对象中各个元素的方法,而无需暴露聚合对象的内部表示。迭代器模式将遍历聚合对象的操作封装在一个迭代器对象中,使客户端能够以一致的方式遍历不同类型的聚合对象。
迭代器模式包含以下几个主要角色:
- 迭代器(Iterator):迭代器是一个抽象接口,它定义了用于遍历聚合对象的方法,包括移动到下一个元素、检查是否还有元素等操作。
- 具体迭代器(Concrete Iterator):具体迭代器是迭代器接口的具体实现,它实现了在特定聚合对象上的遍历方法。
- 聚合(Aggregate):聚合是一个抽象接口,它定义了用于创建迭代器的方法。
- 具体聚合(Concrete Aggregate):具体聚合是聚合接口的具体实现,它包含了一组元素,可以生成相应的具体迭代器。
迭代器模式的主要优点包括:
- 客户端可以通过迭代器以一致的方式遍历不同类型的聚合对象,无需关心具体的数据结构。
- 迭代器模式将遍历操作封装在独立的迭代器对象中,使聚合对象的结构保持私有,提高了封装性。
- 支持多种迭代方式,例如正向遍历、反向遍历、跳跃式遍历等。
以下是一个简单的迭代器模式的 C++ 示例:
#include <iostream>
#include <vector>// 抽象迭代器
class Iterator {
public:virtual int next() = 0;virtual bool hasNext() = 0;
};// 具体迭代器
class ConcreteIterator : public Iterator {
public:ConcreteIterator(const std::vector<int>& collection) : collection_(collection), index_(0) {}int next() override {return collection_[index_++];}bool hasNext() override {return index_ < collection_.size();}private:std::vector<int> collection_;int index_;
};// 抽象聚合
class Aggregate {
public:virtual Iterator* createIterator() = 0;
};// 具体聚合
class ConcreteAggregate : public Aggregate {
public:ConcreteAggregate(const std::vector<int>& collection) : collection_(collection) {}Iterator* createIterator() override {return new ConcreteIterator(collection_);}private:std::vector<int> collection_;
};int main() {std::vector<int> data = {1, 2, 3, 4, 5};ConcreteAggregate aggregate(data);Iterator* iterator = aggregate.createIterator();while (iterator->hasNext()) {std::cout << iterator->next() << " ";}delete iterator;return 0;
}
在这个示例中,我们有一个抽象迭代器 Iterator
,具体迭代器 ConcreteIterator
,抽象聚合 Aggregate
,以及具体聚合 ConcreteAggregate
。
具体聚合 ConcreteAggregate
包含了一组整数元素,并提供了创建迭代器的方法。具体迭代器 ConcreteIterator
实现了在集合上的遍历操作。
在 main
函数中,我们创建了一个包含整数的聚合对象,然后创建了一个迭代器对象,通过迭代器以一致的方式遍历聚合中的元素。这种设计模式使得客户端可以在不关心聚合内部结构的情况下进行遍历操作。
17.中介者模式(Mediator)
中介者模式(Mediator Pattern)是一种行为型设计模式,它用于减少对象之间的直接通信,将对象间的交互通过一个中介者对象进行协调和控制。中介者模式有助于降低系统中对象之间的耦合度,使系统更易维护和扩展。
中介者模式包含以下几个主要角色:
- 中介者(Mediator):中介者是一个接口或抽象类,它定义了对象之间交互的方法,包括注册、通知、协调等。
- 具体中介者(Concrete Mediator):具体中介者是中介者接口的具体实现,它协调各个相关对象之间的交互,并维护对象之间的引用。
- 同事类(Colleague):同事类是相互交互的对象,它们通过中介者来进行通信,而不直接依赖其他同事类。
中介者模式的主要优点包括:
- 降低了对象之间的直接耦合,使系统更加灵活和可维护。
- 可以集中控制对象之间的交互,使系统的交互逻辑更清晰。
- 支持新增和删除同事类,对系统扩展更加友好。
以下是一个简单的中介者模式的 C++ 示例:
#include <iostream>
#include <string>// 抽象中介者
class Mediator {
public:virtual void sendMessage(const std::string& message, class Colleague* colleague) = 0;
};// 具体中介者
class ConcreteMediator : public Mediator {
public:void sendMessage(const std::string& message, class Colleague* colleague) override {std::cout << "Message from " << colleague->getName() << ": " << message << std::endl;}
};// 同事类
class Colleague {
public:Colleague(const std::string& name, Mediator* mediator) : name_(name), mediator_(mediator) {}void sendMessage(const std::string& message) {mediator_->sendMessage(message, this);}const std::string& getName() {return name_;}private:std::string name_;Mediator* mediator_;
};int main() {ConcreteMediator mediator;Colleague colleague1("Colleague 1", &mediator);Colleague colleague2("Colleague 2", &mediator);colleague1.sendMessage("Hello from Colleague 1");colleague2.sendMessage("Hi from Colleague 2");return 0;
}
在这个示例中,我们有一个抽象中介者 Mediator
,具体中介者 ConcreteMediator
,以及两个同事类 Colleague
。同事类通过中介者来发送消息,而不直接与其他同事类进行通信。
中介者模式在协调对象之间的交互时非常有用,特别是在大型系统中,当对象之间的交互关系复杂时,可以通过引入中介者来简化和集中控制交互逻辑。这有助于减少系统的复杂性和提高可维护性。
18.备忘录模式(Memento)
备忘录模式(Memento Pattern)是一种行为型设计模式,它用于捕获一个对象的内部状态,并将其保存在一个外部对象中,从而可以在以后将对象恢复到先前的状态。备忘录模式的关键思想是在不破坏封装性的前提下,将对象的状态保存和恢复。
备忘录模式包含以下几个主要角色:
- 发起人(Originator):发起人是需要保存状态的对象。它有一个创建备忘录和恢复备忘录的方法。发起人的状态可能会变化,可以使用备忘录来保存不同时间点的状态。
- 备忘录(Memento):备忘录是保存发起人状态的对象。它包含了发起人在某一时刻的状态信息。
- 管理者(Caretaker):管理者是用于保存和管理备忘录的对象。它可以保存多个备忘录,以便在需要时恢复发起人的状态。
备忘录模式的主要优点包括:
- 允许在不破坏封装性的情况下保存对象的状态,从而可以实现状态的撤销和恢复操作。
- 提供了一种简单的方式来保存和管理对象的历史状态,支持多次撤销操作。
- 可以降低发起人对象的复杂性,因为状态保存和恢复的逻辑由备忘录和管理者处理。
以下是一个简单的备忘录模式的 C++ 示例:
#include <iostream>
#include <string>// 备忘录类
class Memento {
public:Memento(const std::string& state) : state_(state) {}const std::string& getState() {return state_;}private:std::string state_;
};// 发起人类
class Originator {
public:void setState(const std::string& state) {state_ = state;}const std::string& getState() {return state_;}Memento createMemento() {return Memento(state_);}void restoreMemento(const Memento& memento) {state_ = memento.getState();}private:std::string state_;
};// 管理者类
class Caretaker {
public:void saveMemento(const Memento& memento) {mementos_.push_back(memento);}Memento getMemento(int index) {if (index >= 0 && index < mementos_.size()) {return mementos_[index];}// 返回一个空的备忘录以示错误return Memento("");}private:std::vector<Memento> mementos_;
};int main() {Originator originator;Caretaker caretaker;// 设置初始状态并保存备忘录originator.setState("State 1");caretaker.saveMemento(originator.createMemento());// 修改状态并保存备忘录originator.setState("State 2");caretaker.saveMemento(originator.createMemento());// 恢复到第一个备忘录状态originator.restoreMemento(caretaker.getMemento(0));std::cout << "Current state: " << originator.getState() << std::endl;return 0;
}
在这个示例中,Originator
类表示发起人,它有一个状态需要保存。Memento
类表示备忘录,它可以保存发起人的状态。Caretaker
类表示管理者,它可以保存和管理备忘录。
在 main
函数中,我们设置了初始状态,并保存了备忘录。然后,修改状态并再次保存备忘录。最后,我们恢复到第一个备忘录状态,从而实现了状态的撤销和恢复操作。备忘录模式非常有用,特别是在需要实现撤销和恢复功能的应用中。
19.桥接模式
桥接模式(Bridge Pattern)是一种结构型设计模式,它用于将抽象部分与实现部分分离,以便它们可以独立地变化。桥接模式的目的是将继承关系转化为组合关系,从而减少类之间的耦合度,提高系统的可扩展性。
桥接模式包含以下几个主要角色:
- 抽象部分(Abstraction):抽象部分定义了对实现部分的接口,它维护一个对实现部分的引用,并可以通过委托给实现部分来完成具体的操作。
- 扩展抽象部分(Refined Abstraction):扩展抽象部分是对抽象部分的扩展,通常会增加一些额外的操作。
- 实现部分(Implementor):实现部分定义了具体操作的接口,它可以是一个抽象类或接口,也可以是具体类。
- 具体实现部分(Concrete Implementor):具体实现部分是实现部分的具体实现,它实现了实现部分定义的接口。
桥接模式的主要优点包括:
- 分离抽象部分和实现部分,使它们可以独立变化,增加了系统的灵活性。
- 对于不同的抽象部分,可以选择不同的实现部分,从而实现了多样化的组合。
- 提高了系统的可扩展性,新的抽象部分和实现部分可以方便地添加。
以下是一个简单的桥接模式的 C++ 示例:
#include <iostream>// 实现部分接口
class Implementor {
public:virtual void operationImpl() = 0;
};// 具体实现部分 A
class ConcreteImplementorA : public Implementor {
public:void operationImpl() override {std::cout << "Concrete Implementor A operation" << std::endl;}
};// 具体实现部分 B
class ConcreteImplementorB : public Implementor {
public:void operationImpl() override {std::cout << "Concrete Implementor B operation" << std::endl;}
};// 抽象部分
class Abstraction {
public:Abstraction(Implementor* implementor) : implementor_(implementor) {}virtual void operation() {implementor_->operationImpl();}private:Implementor* implementor_;
};// 扩展抽象部分
class RefinedAbstraction : public Abstraction {
public:RefinedAbstraction(Implementor* implementor) : Abstraction(implementor) {}void additionalOperation() {std::cout << "Additional operation" << std::endl;}
};int main() {Implementor* implA = new ConcreteImplementorA();Implementor* implB = new ConcreteImplementorB();Abstraction* abstraction1 = new Abstraction(implA);abstraction1->operation();Abstraction* abstraction2 = new RefinedAbstraction(implB);abstraction2->operation();static_cast<RefinedAbstraction*>(abstraction2)->additionalOperation();delete implA;delete implB;delete abstraction1;delete abstraction2;return 0;
}
在这个示例中,我们有两个实现部分:ConcreteImplementorA
和 ConcreteImplementorB
,它们实现了 Implementor
接口。然后,我们有抽象部分 Abstraction
和扩展抽象部分 RefinedAbstraction
,它们使用实现部分来完成操作。
在 main
函数中,我们创建了两个不同的实现部分,然后分别将它们与抽象部分和扩展抽象部分组合。通过这种方式,我们可以轻松地改变抽象部分和实现部分的组合,实现不同的操作。这是桥接模式的典型应用。
20.解释器模式(Interpreter)
解释器模式(Interpreter Pattern)是一种行为型设计模式,它用于定义一种语言的文法规则,并提供一个解释器来解释语言中的表达式。该模式将一个句子(语法)分解成一系列的解释动作。解释器模式常用于编译器、解析器和领域特定语言的实现中。
解释器模式包含以下几个主要角色:
- 抽象表达式(Abstract Expression):抽象表达式定义了解释器的接口,包含一个
interpret
方法用于解释表达式。 - 终结符表达式(Terminal Expression):终结符表达式是继承自抽象表达式的类,它实现了
interpret
方法用于解释终结符表达式。 - 非终结符表达式(Non-terminal Expression):非终结符表达式也是继承自抽象表达式的类,它通常包含多个子表达式,并实现了
interpret
方法用于解释非终结符表达式。 - 上下文(Context):上下文包含要解释的文法规则的信息,通常包含解释器所需的数据。
- 客户端(Client):客户端创建并配置解释器,然后使用解释器来解释表达式。
解释器模式的主要优点是可以扩展语言的语法,以及在一些特定领域中解决问题。然而,它也有一些缺点,例如对于复杂的文法规则,解释器模式可能会变得复杂,难以维护。
以下是一个简单的解释器模式的 C++ 示例:
#include <iostream>
#include <unordered_map>// 抽象表达式
class Expression {
public:virtual int interpret(std::unordered_map<char, int>& context) = 0;
};// 终结符表达式
class TerminalExpression : public Expression {
public:TerminalExpression(char variable) : variable_(variable) {}int interpret(std::unordered_map<char, int>& context) override {return context[variable_];}private:char variable_;
};// 非终结符表达式
class NonterminalExpression : public Expression {
public:NonterminalExpression(Expression* left, Expression* right) : left_(left), right_(right) {}int interpret(std::unordered_map<char, int>& context) override {return left_->interpret(context) + right_->interpret(context);}private:Expression* left_;Expression* right_;
};int main() {std::unordered_map<char, int> context;context['a'] = 5;context['b'] = 10;Expression* expression = new NonterminalExpression(new TerminalExpression('a'),new TerminalExpression('b'));int result = expression->interpret(context);std::cout << "Result: " << result << std::endl;delete expression;return 0;
}
在这个示例中,我们创建了一个简单的表达式语言,包括终结符表达式和非终结符表达式。通过解释器模式,我们可以解释这些表达式并计算结果。这个示例是解释器模式的一个简单演示,实际应用中可能会涉及更复杂的语法和解释器。
总结
设计模式是一种通用的解决问题的模板或蓝图,它们用于解决特定类型的问题,并为软件设计提供了可重用的解决方案。在计算机科学中,有23种广泛接受的经典设计模式,它们通常被分为以下几个类别:
-
创建型模式(Creational Patterns):这些模式关注对象的创建机制,以便以适当的方式创建对象,隐藏创建的细节。创建型模式包括:
- 单例模式(Singleton Pattern)
- 工厂模式(Factory Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
-
结构型模式(Structural Patterns):这些模式处理对象之间的组合,以便形成更大的结构。结构型模式包括:
- 适配器模式(Adapter Pattern)
- 桥接模式(Bridge Pattern)
- 组合模式(Composite Pattern)
- 装饰器模式(Decorator Pattern)
- 外观模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
-
行为型模式(Behavioral Patterns):这些模式关注对象之间的通信、职责分配和协作。行为型模式包括:
- 责任链模式(Chain of Responsibility Pattern)
- 命令模式(Command Pattern)
- 解释器模式(Interpreter Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 备忘录模式(Memento Pattern)
- 观察者模式(Observer Pattern)
- 状态模式(State Pattern)
- 策略模式(Strategy Pattern)
- 模板方法模式(Template Method Pattern)
- 访问者模式(Visitor Pattern)
这些经典的设计模式提供了在特定情况下解决问题的有效方法,它们有助于提高软件的可维护性、可扩展性和可重用性。根据应用场景和需求,开发人员可以选择适当的设计模式来构建更健壮、可维护和可扩展的软件系统。
相关文章:

常用的二十种设计模式(下)-C++
设计模式 C中常用的设计模式有很多,设计模式是解决常见问题的经过验证的最佳实践。以下是一些常用的设计模式: 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。工厂模式(…...

C#桶排序算法
前言 桶排序是一种线性时间复杂度的排序算法,它将待排序的数据分到有限数量的桶中,每个桶再进行单独排序,最后将所有桶中的数据按顺序依次取出,即可得到排序结果。 实现原理 首先根据待排序数据,确定需要的桶的数量。…...

快速了解服务器单CPU与双CPU
在当今快节奏的技术环境中,用户们对功能强大且高效的服务器配置需求不断增长。CPU作为构成任何计算基础设施的骨干,服务器的“大脑”,负责执行计算、控制数据流并协调各个组件之间的任务,是服务器选择硬件中的重要一环。因此…...

c# Dictionary、ConcurrentDictionary的使用
Dictionary Dictionary 用于存储键-值对的集合。如果需要高效地存储键-值对并快速查找,请使用 Dictionary。 注意,键必须是唯一的,值可以重复。 using System; using System.Collections.Generic; using System.Linq;class Program {stati…...

大数据中间件——Kafka
Kafka安装配置 首先我们把kafka的安装包上传到虚拟机中: 解压到对应的目录并修改对应的文件名: 首先我们来到kafka的config目录,我们第一个要修改的文件就是server.properties文件,修改内容如下: # Licensed to the …...

HarmonyOS/OpenHarmony原生应用-ArkTS万能卡片组件Slider
滑动条组件,通常用于快速调节设置值,如音量调节、亮度调节等应用场景。该组件从API Version 7开始支持。无子组件 一、接口 Slider(options?: {value?: number, min?: number, max?: number, step?: number, style?: SliderStyle, direction?: Ax…...

SpringCloud: sentinel链路限流
一、配置文件要增加 spring.cloud.sentinel.webContextUnify: false二、在要限流的业务方法上使用SentinelResource注解 package cn.edu.tju.service;import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockExcept…...

UML 中的关系
种类 继承、实现、组合、聚合、关联、依赖 理解 继承和实现的关系强度最大。组合代表着实体之间共同构成一个主体内部的组成部分无法单独支撑,聚合则代表层级更高的一种关联涉及的实体都是独立的个体共同组合起来构成一个主体 个体之间是可以单独工作的。 组合和…...

ChatGPT技术或加剧钓鱼邮件攻击
我们对ChatGPT这一新技术并不陌生,也早就听闻ChatGPT可以通过某种方式绕过安全机制,对目标进行入侵。 ChatGPT的“越狱”技术已经迭代数次,甚至有了先进的“邪恶GPT”WormGPT和FraudGPT,两者都能快速实现钓鱼邮件骗局。 安全分析…...

哨兵1号后向散射系数土壤水分反演
哨兵1号后向散射系数土壤水分反演 数据导入 打开之前预处理之后的VH和VV极化的后向散射系数转存的tiff文件 导入实测点 选择KML转图层 kml文件是由奥维地图导出的.ovkml格式改后缀名得到的 提取采样点的后向散射系数 选择多值提取至点 右键打开点图层的属性表,发现…...

day3:Node.js 基础知识
day3:Node.js 基础知识 文章目录 day3:Node.js 基础知识创建第一个应用事件循环机制异步编程模块系统函数与回调函数路由和全局对象创建第一个应用 实例如下,在你项目的根目录下创建一个叫 helloworld.js 的文件,并写入以下代码: var http = require(http);http.cre…...

【RDMA】librdmacm库和连接建立过程
翻译:rdma_cm - RDMA通信管理器。 概述:rdma_cm是用于建立RDMA传输上的通信的管理器。 说明:RDMA CM是一个用于建立可靠连接和不可靠数据报数据传输的通信管理器。它为建立连接提供了一个RDMA传输中立的接口。该API基于套接字,但…...

如何使用Python抓取PDF文件并自动下载到本地
目录 一、导入必要的库 二、发送HTTP请求并获取PDF文件内容 三、将PDF文件内容写入到本地文件中 四、完整代码示例 五、注意事项 六、错误处理和异常处理 七、进一步优化 总结 在Python中,抓取PDF文件并自动下载到本地需要使用几个不同的库。首先࿰…...

人脸写真FaceChain的简单部署记录(一)
由【让你拥有专属且万能的AI摄影师AI修图师——FaceChain迎来最大版本更新】这篇文章开始出发进行人脸写真的尝试,笔者之前modelscope申请过免费额度,这里有适配的GPU环境可以提供测试。 但是很难抢到GPU资源,需要等待很久,可能才…...

linux虚机新增加磁盘后在系统中查不到
问题描述 在虚机管理平台上对某一linux主机添加了一块硬盘,但在系统中并未显示 通过执行 lsblk,并未看到新增的硬盘信息 解决方法 1. 可通过重启服务器解决 2. 如果不能重启服务器,可重新扫描下 scsi总线 查看总线: ls /s…...

js中隐式类型转换与toPrimitive
前言 我们知道Js的隐式类型转换主要出现在有运算符的情况下【逻辑运算符、关系运算符、算术运算符】。那么在接触toPrimitive之前,我们需要先知道其他值到某个类型值的转换规则。 其他值到数值的转换规则 Boolean: true — 1 false — 0 Null…...

家政系统预约小程序具备哪些功能?
预约家政小程序有这么大的市场需求加上这么多的好处,相信未来发展前景不错。也必将吸引很多商家投资者着手开发属于自己的上门家政APP小程序软件,在实际的开发过程中需要具备哪些功能呢? 一、用户端功能: 1. 用户注册登录&#x…...

【LeetCode】46. 全排列
1 问题 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1: 输入:nums [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例 2: 输入&#x…...

宏电股份RedCap产品亮相迪拜华为MBBF,并参与RedCap全球商用阶段性成果发布
10月10-11日,由华为主办的第十四届全球移动宽带论坛(MBBF)在阿联酋迪拜成功举办。MBBF期间,华为联合宏电股份等产业伙伴集中发布RedCap商用阶段性成果。本次发布是RedCap产业的关键里程碑,标志着RedCap在全球已具备规模…...

Harris图像角点检测
角点检测算法大致有三类:基于灰度图像的角点检测,基于二值图像的角点检测,基于轮廓曲线的角点检测。基于灰度图像的角点检测又可分为基于梯度、基于模板和基于模板梯度组合3类方法,其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够…...

互联网Java工程师面试题·Java 总结篇·第七弹
目录 68、Java 中如何实现序列化,有什么意义? 69、Java 中有几种类型的流? 70、写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数。 71、如何用 Java 代码列出一个目录下所有的文件&a…...

UVa658 It’s not a Bug, it’s a Feature!(Dijkstra)
题意 给出一个包含n个bug的应用程序,以及m个补丁,每个补丁使用两个字符串表示,第一个串表示补丁针对bug的情况,即哪些bug存在,以及哪些bug不存在,第二个串表示补丁对bug的修复情况,即修复了哪些…...

Object 类常用方法
在Java中,java.lang.Object类是所有类的根类,因此所有对象都继承了Object类的方法。以下是Object类中一些常用的方法: equals(Object obj): 用于比较两个对象是否相等。默认实现是比较对象的引用是否相同,但通常需要…...

chromium 52 chrome 各个版本发布功能列表(58-84)
chromium Features 58-84 From https://chromestatus.com/features chromium58 Features:41 ‘allow-top-navigation-by-user-activation’ <iframe sandbox> keyword Adds a new keyword named “allow-top-navigation-by-user-activation” for iframe sandbox, wh…...

python web开发(四): Bootstrap
1.初步了解 别人已经写好的CSS样式,我们可以直接引用 下载 Link-BootStrap 解压,并放入到当前项目中 引用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</tit…...

【EI会议征稿】2024年遥感技术与测量测绘国际学术会议(RSTSM 2024)
2024年遥感技术与测量测绘国际学术会议(RSTSM 2024) 2024 International Conference on Remote Sensing Technology and Survey Mapping 2024年遥感技术与测量测绘国际学术会议(RSTSM 2024)将在2024年1月12-14日于吉林长春召开。…...

灵感:VUE2实现权限按钮控制
运用场景; 根据权限码,实现判断当前用户是否能控制权限按钮 一、在main.JS 里面写入全局指令《自定义权限按钮》 // S 自定义按钮权限 Vue.directive(has, {inserted: function(el, binding) {const buttonList JSON.parse(localStorage.getItem(butt…...

【2023最新版】Python全栈知识点总结
python全栈知识点总结 全栈即指的是全栈工程师,指掌握多种技能,并能利用多种技能独立完成产品的人。就是与这项技能有关的都会,都能够独立的完成。 全栈只是个概念,也分很多种类。真正的全栈工程师涵盖了web开发、DBA 、爬虫 、…...

推荐系统离线评估方法和评估指标,以及在推荐服务器内部实现A/B测试和解决A/B测试资源紧张的方法。还介绍了如何在TensorFlow中进行模型离线评估实践。
文章目录 🌟 离线评估:常用的推荐系统离线评估方法有哪些?🍊 1. RMSE/MSE🍊 2. MAE🍊 3. Precision/Recall/F1-score🍊 4. Coverage🍊 5. Personalization🍊 6. AUC &…...

day1:Node.js 简介
day1:Node.js 简介 文章目录 day1:Node.js 简介Node.js 是什么?Node.js 的历史和发展 ?Node.js 的主要用途和优势 ?Node.js 是什么? 简单的说 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。 Node.js 是一个事…...