上一篇總結了設計模式的創建型模式, 接下來總結一下設計模式的幾種結構型模式。
1. 適配器模式
適配器模式允許將一個類的接口轉換成客戶端所期望的另一個接口。適配器模式通常用于以下情況:
- 當你需要使用一個已經存在的類,但是它的接口與你所需要的接口不匹配時。
- 當你想要創建一個可復用的類,該類與一些不相關或不可預見的類協同工作時。
適配器模式通過引入一個中間層來解決這些問題,這個中間層就是適配器。適配器將客戶端的調用轉換為對被適配對象的調用,從而實現了對接口的適配。
下面是一個簡單的 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()
,實現了對接口的適配。
2. 橋接模式
橋接模式將抽象部分與實現部分分離,使它們可以獨立地變化。這種模式通過將繼承關系轉化為關聯關系來實現,從而使得抽象部分和實現部分可以獨立地變化而不相互影響。
橋接模式主要包含以下角色:
- Abstraction(抽象類):定義了抽象部分的接口,同時維護一個指向實現部分的引用。
- Implementor(實現類接口):定義了實現部分的接口,該接口不一定要與 Abstraction 的接口完全一致。實現類接口并不提供實現,而是聲明了一些基本的操作,由具體的實現類來實現。
- ConcreteImplementor(具體實現類):實現了 Implementor 接口,并具體實現其定義的操作。
- RefinedAbstraction(擴充抽象類):擴充了 Abstraction 中定義的接口,通常通過繼承 Abstraction 來完成。
以下是一個簡單的 C++ 示例,演示了橋接模式的使用:
#include <iostream>// 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()
函數中,我們創建了兩個具體實現類對象和兩個擴充抽象類對象,并調用它們的操作方法,實現了橋接模式的使用。
3. 裝飾模式
裝飾模式允許我們在不改變對象接口的情況下,動態地給對象添加新的功能。裝飾模式通過創建一個包裝對象來包裹真實對象,然后在保持真實對象接口不變的前提下,向其添加額外的功能。
裝飾模式主要包含以下角色:
- Component(組件):定義了一個對象接口,可以為這些對象動態地添加新的功能。
- ConcreteComponent(具體組件):實現了 Component 接口,并定義了具體的對象。
- Decorator(裝飾者):持有一個指向 Component 對象的引用,并且與 Component 接口一致,這樣可以裝飾其他組件。通常是抽象類,它可以為具體組件添加額外的功能。
- ConcreteDecorator(具體裝飾者):擴展了 Decorator 類,實現了裝飾功能。
下面是一個簡單的 C++ 示例,演示了裝飾模式的使用:
#include <iostream>// 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()
函數中,我們創建了一個具體組件對象,并使用具體裝飾者類對其進行裝飾,實現了裝飾模式的使用。
4. 組合模式
組合模式允許我們將對象組合成樹形結構以表示"部分-整體"的層次結構。組合模式使得用戶可以使用統一的方式處理單個對象和對象組合。
組合模式主要包含以下角色:
- Component(組件):定義了組合中的對象的共有接口,可以是抽象類或接口,它提供了一個統一的方法來訪問組合中的所有對象。
- Leaf(葉子):表示組合中的葉子節點對象,葉子節點沒有子節點,它實現了 Component 接口中的方法。
- Composite(復合):表示組合中的容器對象,可以包含其他子節點,它實現了 Component 接口中的方法,并且可以添加、刪除、獲取子節點等操作。
下面是一個簡單的 C++ 示例,演示了組合模式的使用:
#include <iostream>
#include <vector>// 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<Component*> 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()
方法,實現了組合模式的使用。
5. 外觀模式
外觀模式提供了一個統一的接口,用于訪問子系統中的一群接口。外觀模式定義了一個高層接口,這個接口使得子系統更容易使用。
外觀模式的核心思想是將復雜系統的內部邏輯隱藏在一個簡單的接口背后,讓客戶端只需要通過這個簡單的接口與系統交互,而不需要了解系統內部的復雜邏輯。
外觀模式主要包含以下角色:
- Facade(外觀):提供了一個簡單的接口,用于與客戶端交互。外觀對象將客戶端的請求委派給相應的子系統對象來完成具體的工作。
- Subsystem(子系統):實現了系統的功能,是外觀對象的組成部分。客戶端可以直接訪問子系統,但通常通過外觀對象來間接訪問。
下面是一個簡單的 C++ 示例,演示了外觀模式的使用:
#include <iostream>// 子系統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()
方法,實現了對子系統的訪問。通過外觀模式,客戶端可以直接調用外觀對象的方法,而不需要了解系統內部的復雜邏輯,從而簡化了客戶端的使用方式。
6. 享元模式
享元模式旨在通過共享盡可能多的對象來有效支持大量細粒度的對象。
享元模式的核心思想是盡量減少系統中相似對象的數量,通過共享相似對象的狀態來減少內存消耗。在享元模式中,通常將對象的狀態分成內部狀態(Intrinsic State)和外部狀態(Extrinsic State),內部狀態是可以共享的,而外部狀態則是每個對象單獨擁有的。通過這種方式,可以將大量相似對象中的內部狀態共享,從而節省內存空間。
享元模式主要包含以下角色:
- Flyweight(享元):定義了一個接口,用于設置和獲取外部狀態。
- ConcreteFlyweight(具體享元):實現了 Flyweight 接口,并包含內部狀態。
- FlyweightFactory(享元工廠):負責創建和管理享元對象,確保可以正確地共享享元對象。
下面是一個簡單的 C++ 示例,演示了享元模式的使用:
#include <iostream>
#include <map>// 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()
方法。可以看到,當獲取相同內部狀態的享元對象時,工廠會返回同一個對象,從而實現了對相似對象內部狀態的共享。
7. 代理模式
代理模式允許通過一個代理對象來控制對另一個對象的訪問。代理模式常用于需要在訪問對象時增加額外操作的情況,例如延遲加載、權限控制、日志記錄等。
代理模式主要包含以下角色:
- Subject(主題):定義了真實對象和代理對象共同的接口,客戶端通過這個接口訪問真實對象。
- RealSubject(真實主題):定義了真實對象的具體實現。
- Proxy(代理):代理對象,包含了對真實對象的引用,并且與真實對象實現了相同的接口。在客戶端請求時,代理對象會執行額外的操作,然后將請求委派給真實對象。
代理模式通常可以分為靜態代理和動態代理兩種方式。靜態代理是在編譯時就確定代理對象和真實對象的關系,而動態代理是在運行時動態生成代理對象。
下面是一個簡單的 C++ 示例,演示了靜態代理模式的使用:
#include <iostream>// 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()
方法,代理對象處理了客戶端的請求。