上一篇总结了设计模式的创建型模式, 接下来总结一下设计模式的几种结构型模式。
适配器模式允许将一个类的接口转换成客户端所期望的另一个接口。适配器模式通常用于以下情况:
适配器模式通过引入一个中间层来解决这些问题,这个中间层就是适配器。适配器将客户端的调用转换为对被适配对象的调用,从而实现了对接口的适配。
下面是一个简单的 C++ 示例,展示了如何使用适配器模式。
假设我们有一个现有的类 Adaptee,它提供了一个名为 specificRequest() 的方法,但是我们需要与一个接口为 Target 的客户端进行交互。
- #include <iostream>
-
- // Adaptee类,具有特定接口
- class Adaptee {
- public:
- void specificRequest() {
- std::cout << "Adaptee's specific request" << std::endl;
- }
- };
-
- // Target接口
- class Target {
- public:
- virtual void request() = 0;
- };
-
- // Adapter类,将Adaptee转换为Target
- class Adapter : public Target {
- private:
- Adaptee *adaptee;
-
- public:
- Adapter(Adaptee *a) : adaptee(a) {}
-
- void request() override {
- adaptee->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() 方法中调用了 Adaptee 的 specificRequest() 方法。最后,在 main() 函数中,我们将 Adaptee 对象传递给 Adapter 对象,然后将 Adapter 对象传递给客户端函数 client(),实现了对接口的适配。
桥接模式将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过将继承关系转化为关联关系来实现,从而使得抽象部分和实现部分可以独立地变化而不相互影响。
桥接模式主要包含以下角色:
以下是一个简单的 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 << "Refined Abstraction operation - ";
- implementor->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 是实现类接口,定义了实现部分的接口。ConcreteImplementorA 和 ConcreteImplementorB 是具体的实现类,分别实现了 Implementor 接口。Abstraction 是抽象类,它维护了一个指向 Implementor 的引用,并定义了抽象部分的接口。RefinedAbstraction 是扩充抽象类,它继承了 Abstraction 并扩展了其接口。在 main() 函数中,我们创建了两个具体实现类对象和两个扩充抽象类对象,并调用它们的操作方法,实现了桥接模式的使用。
装饰模式允许我们在不改变对象接口的情况下,动态地给对象添加新的功能。装饰模式通过创建一个包装对象来包裹真实对象,然后在保持真实对象接口不变的前提下,向其添加额外的功能。
装饰模式主要包含以下角色:
下面是一个简单的 C++ 示例,演示了装饰模式的使用:
- #include
-
- // Component(组件)接口
- class Component {
- public:
- virtual void operation() = 0;
- };
-
- // ConcreteComponent(具体组件)
- class ConcreteComponent : public Component {
- public:
- void operation() override {
- std::cout << "ConcreteComponent operation" << std::endl;
- }
- };
-
- // Decorator(装饰者)抽象类
- class Decorator : public Component {
- protected:
- Component *component;
-
- public:
- Decorator(Component *comp) : component(comp) {}
-
- void operation() override {
- if (component != nullptr) {
- component->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" << std::endl;
- }
- };
-
- int main() {
- Component *component = new ConcreteComponent();
- Decorator *decoratorA = new ConcreteDecoratorA(component);
- Decorator *decoratorB = new ConcreteDecoratorB(decoratorA);
-
- decoratorB->operation();
-
- delete decoratorB;
- delete decoratorA;
- delete component;
-
- return 0;
- }
在这个示例中,Component 是组件接口,定义了操作方法。ConcreteComponent 是具体组件类,实现了 Component 接口的方法。Decorator 是装饰者抽象类,持有一个指向 Component 对象的引用,并且与 Component 接口一致,以便可以装饰其他组件。ConcreteDecoratorA 和 ConcreteDecoratorB 是具体装饰者类,它们扩展了 Decorator 类,并实现了装饰功能。
在 main() 函数中,我们创建了一个具体组件对象,并使用具体装饰者类对其进行装饰,实现了装饰模式的使用。
组合模式允许我们将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户可以使用统一的方式处理单个对象和对象组合。
组合模式主要包含以下角色:
下面是一个简单的 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" << std::endl;
- for (Component *child : children) {
- child->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() 方法,实现了组合模式的使用。
外观模式提供了一个统一的接口,用于访问子系统中的一群接口。外观模式定义了一个高层接口,这个接口使得子系统更容易使用。
外观模式的核心思想是将复杂系统的内部逻辑隐藏在一个简单的接口背后,让客户端只需要通过这个简单的接口与系统交互,而不需要了解系统内部的复杂逻辑。
外观模式主要包含以下角色:
下面是一个简单的 C++ 示例,演示了外观模式的使用:
- #include
-
- // 子系统A
- class SubsystemA {
- public:
- void operationA() {
- std::cout << "SubsystemA operation" << std::endl;
- }
- };
-
- // 子系统B
- class 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;
- }
在这个示例中,SubsystemA 和 SubsystemB 分别是两个子系统,它们分别实现了系统的一部分功能。Facade 是外观类,它提供了一个简单的接口 operation(),内部调用了 SubsystemA 和 SubsystemB 的方法来完成工作。
在 main() 函数中,我们创建了一个外观对象 facade,并调用了其 operation() 方法,实现了对子系统的访问。通过外观模式,客户端可以直接调用外观对象的方法,而不需要了解系统内部的复杂逻辑,从而简化了客户端的使用方式。
享元模式旨在通过共享尽可能多的对象来有效支持大量细粒度的对象。
享元模式的核心思想是尽量减少系统中相似对象的数量,通过共享相似对象的状态来减少内存消耗。在享元模式中,通常将对象的状态分成内部状态(Intrinsic State)和外部状态(Extrinsic State),内部状态是可以共享的,而外部状态则是每个对象单独拥有的。通过这种方式,可以将大量相似对象中的内部状态共享,从而节省内存空间。
享元模式主要包含以下角色:
下面是一个简单的 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<int, Flyweight*> 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() 方法。可以看到,当获取相同内部状态的享元对象时,工厂会返回同一个对象,从而实现了对相似对象内部状态的共享。
代理模式允许通过一个代理对象来控制对另一个对象的访问。代理模式常用于需要在访问对象时增加额外操作的情况,例如延迟加载、权限控制、日志记录等。
代理模式主要包含以下角色:
代理模式通常可以分为静态代理和动态代理两种方式。静态代理是在编译时就确定代理对象和真实对象的关系,而动态代理是在运行时动态生成代理对象。
下面是一个简单的 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() 方法,代理对象处理了客户端的请求。