上一篇总结了设计模式的创建型模式, 接下来总结一下设计模式的几种结构型模式。

1. 适配器模式

适配器模式允许将一个类的接口转换成客户端所期望的另一个接口。适配器模式通常用于以下情况:

  1. 当你需要使用一个已经存在的类,但是它的接口与你所需要的接口不匹配时。
  2. 当你想要创建一个可复用的类,该类与一些不相关或不可预见的类协同工作时。

适配器模式通过引入一个中间层来解决这些问题,这个中间层就是适配器。适配器将客户端的调用转换为对被适配对象的调用,从而实现了对接口的适配。

下面是一个简单的 C++ 示例,展示了如何使用适配器模式。

假设我们有一个现有的类 Adaptee,它提供了一个名为 specificRequest() 的方法,但是我们需要与一个接口为 Target 的客户端进行交互。

#include // Adaptee类,具有特定接口class Adaptee {public:void specificRequest() {std::cout << "Adaptee's specific request" <specificRequest();}};// Client使用Target接口void client(Target *target) {target->request();}int main() {Adaptee *adaptee = new Adaptee();Target *adapter = new Adapter(adaptee);client(adapter);delete adaptee;delete adapter;return 0;}

在这个例子中,Adaptee 类是我们需要适配的类,它提供了 specificRequest() 方法。Target 是我们需要与之交互的接口。Adapter 类实现了 Target 接口,并持有一个 Adaptee 对象,在 request() 方法中调用了 AdapteespecificRequest() 方法。最后,在 main() 函数中,我们将 Adaptee 对象传递给 Adapter 对象,然后将 Adapter 对象传递给客户端函数 client(),实现了对接口的适配。

2. 桥接模式

桥接模式将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过将继承关系转化为关联关系来实现,从而使得抽象部分和实现部分可以独立地变化而不相互影响。

桥接模式主要包含以下角色:

  1. Abstraction(抽象类):定义了抽象部分的接口,同时维护一个指向实现部分的引用。
  2. Implementor(实现类接口):定义了实现部分的接口,该接口不一定要与 Abstraction 的接口完全一致。实现类接口并不提供实现,而是声明了一些基本的操作,由具体的实现类来实现。
  3. ConcreteImplementor(具体实现类):实现了 Implementor 接口,并具体实现其定义的操作。
  4. RefinedAbstraction(扩充抽象类):扩充了 Abstraction 中定义的接口,通常通过继承 Abstraction 来完成。

以下是一个简单的 C++ 示例,演示了桥接模式的使用:

#include // Implementor(实现类接口)class Implementor {public:virtual void operationImpl() = 0;};// ConcreteImplementorA(具体实现类A)class ConcreteImplementorA : public Implementor {public:void operationImpl() override {std::cout << "Concrete Implementor A operation" << std::endl;}};// ConcreteImplementorB(具体实现类B)class ConcreteImplementorB : public Implementor {public:void operationImpl() override {std::cout << "Concrete Implementor B operation" << std::endl;}};// Abstraction(抽象类)class Abstraction {protected:Implementor *implementor;public:Abstraction(Implementor *impl) : implementor(impl) {}virtual void operation() = 0;};// RefinedAbstraction(扩充抽象类)class RefinedAbstraction : public Abstraction {public:RefinedAbstraction(Implementor *impl) : Abstraction(impl) {}void operation() override {std::cout <operationImpl();}};int main() {Implementor *implA = new ConcreteImplementorA();Implementor *implB = new ConcreteImplementorB();Abstraction *abstraction1 = new RefinedAbstraction(implA);Abstraction *abstraction2 = new RefinedAbstraction(implB);abstraction1->operation();abstraction2->operation();delete implA;delete implB;delete abstraction1;delete abstraction2;return 0;}

在这个示例中,Implementor 是实现类接口,定义了实现部分的接口。ConcreteImplementorAConcreteImplementorB 是具体的实现类,分别实现了 Implementor 接口。Abstraction 是抽象类,它维护了一个指向 Implementor 的引用,并定义了抽象部分的接口。RefinedAbstraction 是扩充抽象类,它继承了 Abstraction 并扩展了其接口。在 main() 函数中,我们创建了两个具体实现类对象和两个扩充抽象类对象,并调用它们的操作方法,实现了桥接模式的使用。

3. 装饰模式

装饰模式允许我们在不改变对象接口的情况下,动态地给对象添加新的功能。装饰模式通过创建一个包装对象来包裹真实对象,然后在保持真实对象接口不变的前提下,向其添加额外的功能。

装饰模式主要包含以下角色:

  1. Component(组件):定义了一个对象接口,可以为这些对象动态地添加新的功能。
  2. ConcreteComponent(具体组件):实现了 Component 接口,并定义了具体的对象。
  3. Decorator(装饰者):持有一个指向 Component 对象的引用,并且与 Component 接口一致,这样可以装饰其他组件。通常是抽象类,它可以为具体组件添加额外的功能。
  4. ConcreteDecorator(具体装饰者):扩展了 Decorator 类,实现了装饰功能。

下面是一个简单的 C++ 示例,演示了装饰模式的使用:

#include // Component(组件)接口class Component {public:virtual void operation() = 0;};// ConcreteComponent(具体组件)class ConcreteComponent : public Component {public:void operation() override {std::cout << "ConcreteComponent operation" <operation();}}};// ConcreteDecoratorA(具体装饰者A)class ConcreteDecoratorA : public Decorator {public:ConcreteDecoratorA(Component *comp) : Decorator(comp) {}void operation() override {Decorator::operation();addFunctionality();}void addFunctionality() {std::cout << "Added functionality by ConcreteDecoratorA" << std::endl;}};// ConcreteDecoratorB(具体装饰者B)class ConcreteDecoratorB : public Decorator {public:ConcreteDecoratorB(Component *comp) : Decorator(comp) {}void operation() override {Decorator::operation();addMoreFunctionality();}void addMoreFunctionality() {std::cout << "Added more functionality by ConcreteDecoratorB" <operation();delete decoratorB;delete decoratorA;delete component;return 0;}

在这个示例中,Component 是组件接口,定义了操作方法。ConcreteComponent 是具体组件类,实现了 Component 接口的方法。Decorator 是装饰者抽象类,持有一个指向 Component 对象的引用,并且与 Component 接口一致,以便可以装饰其他组件。ConcreteDecoratorAConcreteDecoratorB 是具体装饰者类,它们扩展了 Decorator 类,并实现了装饰功能。

main() 函数中,我们创建了一个具体组件对象,并使用具体装饰者类对其进行装饰,实现了装饰模式的使用。

4. 组合模式

组合模式允许我们将对象组合成树形结构以表示”部分-整体”的层次结构。组合模式使得用户可以使用统一的方式处理单个对象和对象组合。

组合模式主要包含以下角色:

  1. Component(组件):定义了组合中的对象的共有接口,可以是抽象类或接口,它提供了一个统一的方法来访问组合中的所有对象。
  2. Leaf(叶子):表示组合中的叶子节点对象,叶子节点没有子节点,它实现了 Component 接口中的方法。
  3. Composite(复合):表示组合中的容器对象,可以包含其他子节点,它实现了 Component 接口中的方法,并且可以添加、删除、获取子节点等操作。

下面是一个简单的 C++ 示例,演示了组合模式的使用:

#include #include // Component(组件)接口class Component {public:virtual void operation() = 0;};// Leaf(叶子)类class Leaf : public Component {public:void operation() override {std::cout << "Leaf operation" << std::endl;}};// Composite(复合)类class Composite : public Component {private:std::vector children;public:void add(Component *component) {children.push_back(component);}void remove(Component *component) {// 在实际应用中,通常需要在这里实现查找并删除子节点的逻辑// 这里简化为直接删除对应指针children.erase(std::remove(children.begin(), children.end(), component), children.end());}Component* getChild(int index) {if (index >= 0 && index < children.size()) {return children[index];}return nullptr;}void operation() override {std::cout << "Composite operation" <operation();}}};int main() {Component *leaf1 = new Leaf();Component *leaf2 = new Leaf();Component *leaf3 = new Leaf();Composite *composite1 = new Composite();composite1->add(leaf1);composite1->add(leaf2);Composite *composite2 = new Composite();composite2->add(leaf3);Composite *composite = new Composite();composite->add(composite1);composite->add(composite2);composite->operation();delete composite;delete composite2;delete composite1;delete leaf3;delete leaf2;delete leaf1;return 0;}

在这个示例中,Component 是组件接口,定义了操作方法。Leaf 类是叶子类,它实现了 Component 接口的方法。Composite 类是复合类,它实现了 Component 接口,并且可以包含其他子节点。在 main() 函数中,我们创建了多个叶子对象和复合对象,将它们组合成了树形结构,并调用了根节点的 operation() 方法,实现了组合模式的使用。

5. 外观模式

外观模式提供了一个统一的接口,用于访问子系统中的一群接口。外观模式定义了一个高层接口,这个接口使得子系统更容易使用。

外观模式的核心思想是将复杂系统的内部逻辑隐藏在一个简单的接口背后,让客户端只需要通过这个简单的接口与系统交互,而不需要了解系统内部的复杂逻辑。

外观模式主要包含以下角色:

  1. Facade(外观):提供了一个简单的接口,用于与客户端交互。外观对象将客户端的请求委派给相应的子系统对象来完成具体的工作。
  2. Subsystem(子系统):实现了系统的功能,是外观对象的组成部分。客户端可以直接访问子系统,但通常通过外观对象来间接访问。

下面是一个简单的 C++ 示例,演示了外观模式的使用:

#include // 子系统Aclass SubsystemA {public:void operationA() {std::cout << "SubsystemA operation" << std::endl;}};// 子系统Bclass SubsystemB {public:void operationB() {std::cout << "SubsystemB operation" << std::endl;}};// 外观类class Facade {private:SubsystemA subsystemA;SubsystemB subsystemB;public:void operation() {subsystemA.operationA();subsystemB.operationB();}};int main() {Facade facade;facade.operation();return 0;}

在这个示例中,SubsystemASubsystemB 分别是两个子系统,它们分别实现了系统的一部分功能。Facade 是外观类,它提供了一个简单的接口 operation(),内部调用了 SubsystemASubsystemB 的方法来完成工作。

main() 函数中,我们创建了一个外观对象 facade,并调用了其 operation() 方法,实现了对子系统的访问。通过外观模式,客户端可以直接调用外观对象的方法,而不需要了解系统内部的复杂逻辑,从而简化了客户端的使用方式。

6. 享元模式

享元模式旨在通过共享尽可能多的对象来有效支持大量细粒度的对象。

享元模式的核心思想是尽量减少系统中相似对象的数量,通过共享相似对象的状态来减少内存消耗。在享元模式中,通常将对象的状态分成内部状态(Intrinsic State)和外部状态(Extrinsic State),内部状态是可以共享的,而外部状态则是每个对象单独拥有的。通过这种方式,可以将大量相似对象中的内部状态共享,从而节省内存空间。

享元模式主要包含以下角色:

  1. Flyweight(享元):定义了一个接口,用于设置和获取外部状态。
  2. ConcreteFlyweight(具体享元):实现了 Flyweight 接口,并包含内部状态。
  3. FlyweightFactory(享元工厂):负责创建和管理享元对象,确保可以正确地共享享元对象。

下面是一个简单的 C++ 示例,演示了享元模式的使用:

#include #include // Flyweight(享元)接口class Flyweight {public:virtual void operation(int extrinsicState) = 0;};// ConcreteFlyweight(具体享元)类class ConcreteFlyweight : public Flyweight {private:int intrinsicState; // 内部状态,可以共享public:ConcreteFlyweight(int state) : intrinsicState(state) {}void operation(int extrinsicState) override {std::cout << "ConcreteFlyweight with intrinsic state " << intrinsicState<< " and extrinsic state " << extrinsicState << std::endl;}};// FlyweightFactory(享元工厂)类class FlyweightFactory {private:std::map flyweights;public:Flyweight* getFlyweight(int key) {if (flyweights.find(key) == flyweights.end()) {flyweights[key] = new ConcreteFlyweight(key);}return flyweights[key];}~FlyweightFactory() {for (auto& pair : flyweights) {delete pair.second;}flyweights.clear();}};int main() {FlyweightFactory factory;Flyweight* flyweight1 = factory.getFlyweight(1);flyweight1->operation(100);Flyweight* flyweight2 = factory.getFlyweight(2);flyweight2->operation(200);Flyweight* flyweight3 = factory.getFlyweight(1);flyweight3->operation(300);return 0;}

在这个示例中,Flyweight 是享元接口,定义了操作方法。ConcreteFlyweight 是具体享元类,实现了 Flyweight 接口,并包含内部状态。FlyweightFactory 是享元工厂类,负责创建和管理享元对象。

main() 函数中,我们创建了一个享元工厂对象 factory,然后通过工厂获取了几个具体享元对象,并分别调用它们的 operation() 方法。可以看到,当获取相同内部状态的享元对象时,工厂会返回同一个对象,从而实现了对相似对象内部状态的共享。

7. 代理模式

代理模式允许通过一个代理对象来控制对另一个对象的访问。代理模式常用于需要在访问对象时增加额外操作的情况,例如延迟加载、权限控制、日志记录等。

代理模式主要包含以下角色:

  1. Subject(主题):定义了真实对象和代理对象共同的接口,客户端通过这个接口访问真实对象。
  2. RealSubject(真实主题):定义了真实对象的具体实现。
  3. Proxy(代理):代理对象,包含了对真实对象的引用,并且与真实对象实现了相同的接口。在客户端请求时,代理对象会执行额外的操作,然后将请求委派给真实对象。

代理模式通常可以分为静态代理和动态代理两种方式。静态代理是在编译时就确定代理对象和真实对象的关系,而动态代理是在运行时动态生成代理对象。

下面是一个简单的 C++ 示例,演示了静态代理模式的使用:

#include // Subject(主题)接口class Subject {public:virtual void request() = 0;};// RealSubject(真实主题)类class RealSubject : public Subject {public:void request() override {std::cout << "RealSubject handles request." << std::endl;}};// Proxy(代理)类class Proxy : public Subject {private:RealSubject realSubject;public:void request() override {// 在真实主题处理请求前可以执行额外的操作std::cout << "Proxy handles request." << std::endl;// 委派给真实主题处理请求realSubject.request();}};int main() {Proxy proxy;proxy.request();return 0;}

在这个示例中,Subject 是主题接口,定义了操作方法。RealSubject 是真实主题类,实现了 Subject 接口的方法。Proxy 是代理类,包含了对真实主题对象的引用,并且实现了与真实主题相同的接口。在 request() 方法中,代理对象执行了额外的操作,然后将请求委派给真实主题处理。

main() 函数中,我们创建了一个代理对象 proxy,并调用了其 request() 方法,代理对象处理了客户端的请求。