(資料圖片僅供參考)
阿里妹導讀
本文從設計原則、創建型模式、結構型模式、行為模式四個方向講述C++的設計模式。
從設計原則
單一職責原則
定義:單一職責原則[1],所謂職責是指類變化的原因。如果一個類有多于一個的動機被改變,那么這個類就具有多于一個的職責。而單一職責原則就是指一個類或者模塊應該有且只有一個改變的原因。
bad case:IPhone類承擔了協議管理(Dial、HangUp)、數據傳送(Chat)。
good case:
里式替換原則
定義:里氏代換原則[2](Liskov Substitution Principle LSP),任何基類可以出現的地方,子類一定可以出現。
bad case:ToyGun繼承了AbstractGun,但Solider在調用KillEnemy()時會報錯(ToyGun無法KillEnemy),即ToyGun無法完全行使AbstractGun的職責。
good case:AbstractToy中將聲音、形狀都委托給AbstractGun處理。
如果子類不能完整地實現父類的方法,或者父類的某些方法在子類中已經發生“畸變”,則建議斷開父子繼承關系,采用依賴、聚集、組合等關系代替。
依賴倒置原則
定義:依賴倒置原則[3](Dependence Inversion Principle)是程序要依賴于抽象接口,不要依賴于具體實現。簡單的說就是要求對抽象進行編程,不要對實現進行編程,這樣就降低了客戶與實現模塊間的耦合。
bad case:Driver強依賴于奔馳車。
good case:
接口隔離原則
定義:接口隔離原則[4],客戶端不應該依賴它不需要的接口。一個類對另一個類的依賴應該建立在最小的接口上。
bad case:星探尋找美女的類圖,其中IpettyGirl過于龐大,容納過多可變的因素。
good case:通過拆分接口,提高了靈活性、可維護性。
迪米特法則
定義:迪米特法則[5](Law of Demeter)又叫作最少知識原則(The Least Knowledge Principle),一個類對于其他類知道的越少越好,就是說一個對象應當對其他對象有盡可能少的了解,只和朋友通信,不和陌生人說話。
bad case:Teacher要求GroupLeader清點女生的數量,這里Teacher不應該依賴于Gril。
good case:
開閉原則
定義:開閉原則[6],在面向對象編程領域中,規定“軟件中的對象(類,模塊,函數等等)應該對于擴展是開放的,但是對于修改是封閉的”。
以一個書店售書類圖為例,當在書店要增加一個打折操作時。
bad case:修改實現類,在IBook上增加一個方法GetOffPrice()
good case:通過擴展實現變化,增加一個子類OffNovelBook
創建型模式
工廠方法
定義一個用于創建對象的接口Product* CreateProduct(),讓子類決定實例化哪一個類。工廠方法模式讓類的實例化延遲到子類中進行,從而避免了在父類中創建對象時出現類名稱緊耦合的問題,同時提高了代碼的可擴展性和可維護性。(工廠方法的好處就是解耦,當我們修改了具體的類,對調用方而言完全不用修改)
class Product { // 抽象產品public: virtual void Method() = 0;};class ConcreteProduct1 : public Product {public: void Method() { cout << "ConcreteProduct1" << endl; }};class ConcreteProduct2 : public Product {public: void Method() { cout << "ConcreteProduct2" << endl; }};class Factory { // 抽象工廠public: virtual Product* CreateProduct() = 0;};class ConcreteFactory1 : public Factory {public: Product* CreateProduct() {return new ConcreteProduct1(); }};class ConcreteFactory2 : public Factory {public: Product* CreateProduct() {return new ConcreteProduct2(); }};int main () { Factory *factory1 = new ConcreteFactory1(); Factory *factory2 = new ConcreteFactory2(); Product *product1 = factory1->CreateProduct(); Product *product2 = factory2->CreateProduct(); product1->Method(); product2->Method();}抽象工廠
為創建一組相關或相互依賴的對象提供一個接口,而且無須指定他們的具體類。(工廠方法模式針對的是一個產品等級結構;而抽象工廠模式針對的是多個產品等級結構。抽象工廠模式主要用來實現生產一系列的產品。)
class AbstractProductA { public: virtual ~AbstractProductA(){}; virtual std::string FunctionA() const = 0;};class ProductA1 : public AbstractProductA { public: std::string FunctionA() const override { return "The result of the product A1."; }};class ProductA2 : public AbstractProductA { std::string FunctionA() const override { return "The result of the product A2."; }};class AbstractProductB { public: virtual ~AbstractProductB(){}; virtual std::string FunctionB() const = 0;};class ProductB1 : public AbstractProductB { public: std::string FunctionB() const override { return "The result of the product B1."; }};class ProductB2 : public AbstractProductB { public: std::string FunctionB() const override { return "The result of the product B2."; }};class AbstractFactory { public: virtual AbstractProductA *CreateProductA() const = 0; virtual AbstractProductB *CreateProductB() const = 0;};class Factory1 : public AbstractFactory { public: AbstractProductA *CreateProductA() const override { return new ProductA1(); } AbstractProductB *CreateProductB() const override { return new ProductB1(); }};class Factory2 : public AbstractFactory { public: AbstractProductA *CreateProductA() const override { return new ProductA2(); } AbstractProductB *CreateProductB() const override { return new ProductB2(); }};void Client(const AbstractFactory &factory) { const AbstractProductA *productA = factory.CreateProductA(); const AbstractProductB *productB = factory.CreateProductB(); std::cout << productA->FunctionA() << "\n"; std::cout << productB->FunctionB() << "\n"; delete productA; delete productB;}int main() { Factory1 *f1 = new Factory1(); Client(*f1); delete f1; Factory2 *f2 = new Factory2(); Client(*f2); delete f2; return 0;}生成器/建造者
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。(建造者模式關注的是零件類型和裝配工藝(順序))
class Product1{public: std::vector原型
用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。(原型模式實現的是一個Clone 接口,注意是接口,也就是基于多態的 Clone 虛函數。)
class Prototype { protected: string mPrototypeName; float mPrototypeField;public: Prototype() {} Prototype(string prototypeName) : mPrototypeName(prototypeName) { } virtual ~Prototype() {} virtual Prototype *Clone() const = 0; virtual void Function(float prototype_field) { this->mPrototypeField = prototype_field; std::cout << "Call Function from " << mPrototypeName << " with field : " << prototype_field << std::endl; }};class ConcretePrototype1 : public Prototype {private: float mConcretePrototypeField;public: ConcretePrototype1(string prototypeName, float concretePrototypeField) : Prototype(prototypeName), mConcretePrototypeField(concretePrototypeField) { } Prototype *Clone() const override { return new ConcretePrototype1(*this); }};class ConcretePrototype2 : public Prototype {private: float mConcretePrototypeField;public: ConcretePrototype2(string prototypeName, float concretePrototypeField) : Prototype(prototypeName), mConcretePrototypeField(concretePrototypeField) { } Prototype *Clone() const override { return new ConcretePrototype2(*this); }};class PrototypeFactory {private: std::unordered_map單例
單例模式是指在整個系統生命周期內,保證一個類只能產生一個實例,確保該類的唯一性。
class SingleInstance{public: static SingleInstance* GetInstance(); void Print();private: // 構造、析構、拷貝構造和賦值構造均為私有,防止構造多個對象 SingleInstance(); ~SingleInstance(); SingleInstance(const SingleInstance &instance); const SingleInstance &operator=(const SingleInstance &instance); static SingleInstance* mInstancePtr;};SingleInstance* SingleInstance::mInstancePtr = nullptr;SingleInstance* SingleInstance::GetInstance(){ if (mInstancePtr == nullptr) mInstancePtr = new SingleInstance(); return mInstancePtr;}擴展:
在2/3例模式(一個類能夠產生2-3個實例)的情況下,則在私有屬性內定義一個類的List,List內包含了所有的實例。
在普通類的情況下,想實現單例模式。可以實現一個Manager管理類,Manager類在初始化時生成各個普通類的實例,然后通過GetInstance()統一返回實例。
結構型模式
適配器
將一個類的接口轉換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。適配器模式是一個補償模式,或者說是一個“補救”模式,通常用來解決接口不相容的問題。
class Target { // Target,客戶期望的接口,可以使具體或抽象的類,也可以是接口public: virtual void Request() = 0; virtual ~Target(){};};class Adaptee { // 需適配的類public: void SpecificRequest() { cout << "Adaptee" << endl; }};class Adapter : public Target { // 通過內部包裝一個Adaptee對象,把源接口轉換為目標接口:private: Adaptee* mAdaptee;public: Adapter() { mAdaptee = new Adaptee(); } void Request() { mAdaptee->SpecificRequest(); } // 調用Request()方法會轉換成調用adaptee.SpecificRequest() ~Adapter() { delete mAdaptee; }};int main() { Target* target = new Adapter(); target->Request(); delete target; return 0;}說明:
Target:客戶期待的接口
Adapter:通過在內部包裝一個Adaptee對象,把源接口轉換成目標接口
Adaptee:需要適配的類
橋接/橋梁
將抽象和實現解耦,使得兩者可以獨立的變化。
class OperationSys{public: OperationSys() {} ~OperationSys(){} virtual void SetName() = 0; virtual void OutputName() = 0;protected: std::string mName;};class IOSSystem:public OperationSys{public: IOSSystem() {} ~IOSSystem() {} virtual void SetName() { mName = "IOS-SYS"; } virtual void OutputName() { std::cout << "I am IOS,name:" << mName << std::endl; }};class HarmonySystem :public OperationSys{public: HarmonySystem() {} ~HarmonySystem() {} virtual void SetName() { mName = "HarmonySystem"; } virtual void OutputName() { std::cout << "I am Harmony operation system,name:" << mName << std::endl; }};class AndroidSystem :public OperationSys{public: AndroidSystem() {} ~AndroidSystem() {} virtual void SetName() { mName = "AndroidSystem"; } virtual void OutputName() { std::cout << "I am Android operation system,name:" << mName << std::endl; }};class Phone{public: Phone() {} ~Phone(){} virtual void SetName() = 0; virtual void OutputName() = 0; virtual void SetOperation(OperationSys* sys) { mOperSystem = sys; } virtual void OutputSysName() = 0;protected: OperationSys* mOperSystem; std::string mName;};class IPhone :public Phone{public: IPhone() {} ~IPhone(){} virtual void SetName() { mName = "IPhone"; } virtual void OutputName() { std::cout << "I am IPhone,Name:" << mName << std::endl; } virtual void SetOperation(OperationSys* sys) { mOperSystem = sys; } virtual void OutputSysName() { mOperSystem->OutputName(); }};class HwPhone :public Phone{public: HwPhone() {} ~HwPhone() {} virtual void SetName() { mName = "HuaWeiPhone"; } virtual void OutputName() { std::cout << "I am HuaWei,Name:" << mName << std::endl; } virtual void SetOperation(OperationSys* sys) { mOperSystem = sys; } virtual void OutputSysName() { mOperSystem->OutputName(); }};class MiPhone :public Phone{public: MiPhone() {} ~MiPhone() {} virtual void SetName() { mName = "MiPhone"; } virtual void OutputName() { std::cout << "I am XiaoMi,Name:" << mName << std::endl; } virtual void SetOperation(OperationSys* sys) { mOperSystem = sys; } virtual void OutputSysName() { mOperSystem->OutputName(); }};int main(int argc, char* argv[]){ IOSSystem* iSys = new IOSSystem(); iSys->SetName(); IPhone* iPhone = new IPhone(); iPhone->SetName(); iPhone->SetOperation(iSys); HarmonySystem* hSys = new HarmonySystem(); hSys->SetName(); HwPhone* wPhone = new HwPhone(); wPhone->SetName(); wPhone->SetOperation(hSys); AndroidSystem* aSys = new AndroidSystem(); aSys->SetName(); MiPhone* mPhone = new MiPhone(); mPhone->SetName(); mPhone->SetOperation(aSys); iPhone->OutputName(); iPhone->OutputSysName(); wPhone->OutputName(); wPhone->OutputSysName(); mPhone->OutputName(); mPhone->OutputSysName(); return 0;}說明:
Abstraction:抽象化角色,主要職責是定義一個角色的行為,同時保存一個對實現化角色的引用,一般為抽象類
Implementor:實現化角色,接口或抽象類,定義角色必須的行為和屬性
RefinedAbstraction:修正抽象化角色,引用實現化角色對抽象化角色進行修正
ConcreteImplementor:具體實現化角色,實現接口或抽象類定義的方法和屬性
組合
用來描述部分與整體的關系(將對象組合成樹形結構以表示“部分-整體”的層次結構,使得用戶對單個對象和組合對象的使用具有一致性)
使用場景:
希望表示對象的部分-整體層次結構
希望用戶忽略組合對象與單個對象的不同,用戶將統一地使用組合結構中的所有對象
class Component{protected: string mName;public: Component(string name) : mName(name) {} virtual ~Component() {} virtual void Operation() = 0; virtual void Add(Component *com) = 0; virtual void Remove(Component *com) = 0; virtual Component *Getchild(int index) = 0; virtual string GetName() { return mName; } virtual void ShowChilds() = 0;};class Leaf : public Component // 樹葉結構{public: Leaf(string name) : Component(name) {} void Operation() { cout << "name : " << mName << endl; } void Add(Component *com) {} void Remove(Component *com) {} Component *GetChild(int index) { return NULL; } void ShowChilds() {}};class Composite : public Component // 樹枝結構{private: vector說明:
Component:抽象構件角色
Leaf:葉子構件,遍歷的最小單位
Composite:樹枝構件,組合樹枝節點和葉子節點形成一個樹形結構
裝飾
動態地給一個對象添加一些額外的職責(用來給對象增加某些特性或者對被裝飾對象進行某些修改)。裝飾模式是繼承關系的一個替代方案,但多層的裝飾是比較復雜的。
class Component {public: virtual ~Component() {}; virtual void Operate() = 0;};class ConcreteComponent : public Component{public: void Operate() override { cout << "do something" << endl;}};class Decorator : public Component{public: Decorator(Component* component) : mComponent(component) {} void Operate() override{ mComponent->Operate(); }private: Component* mComponent = nullptr;};class ConcreteDecorator1 : public Decorator {public: ConcreteDecorator1(Component* component) : Decorator(component) {} void Operate() override { method1(); Decorator::Operate(); }private: void method1() { cout << "method1 修飾" << endl; }};class ConcreteDecorator2 : public Decorator {public: ConcreteDecorator2(Component* component) : Decorator(component) {} void Operate() override { method2(); Decorator::Operate(); }private: void method2() { cout << "method2 修飾" << endl; }};int main(){ Component* component = new ConcreteComponent(); component = new ConcreteDecorator1(component); component = new ConcreteDecorator2(component); component->Operate(); delete component;}說明:
Component抽象構件
ConcreteComponent具體構件,需要裝飾的對象
Decorator裝飾角色,屬性中必然有一個private變量指向Component抽象構件
具體裝飾角色(ConcreteDecoratorA、ConcreteDecoratorB)
外觀/門面
門面模式要求一個子系統的外部與其內部的通信必須通過一個統一的對象進行。門面模式提供一個高層次的接口,使得子系統更易于使用。簡單地說,門面對象是外界訪問子系統內部的唯一通道,不管系統內部是多么雜亂無章,只要門面對象在,就可以做到“金玉其外,敗絮其中”。
class A{public:void DoSomething() { cout << "class A is doing something" << endl; }};class B{public:void DoSomething() { cout << "class B is doing something" << endl; }};class C{public:void DoSomething() { cout << "class C is doing something" << endl; }};class Facade{public:Facade(){ a = make_shared(); b = make_shared(); c = make_shared說明:
Facade門面角色,本角色會將所有從客戶端發來的請求委派到相應的子系統去,也就是說該角色沒有實際的業務邏輯,只是一個委托類。
subsystem子系統角色,子系統并不知道門面的存在,對于子系統而言,門面僅僅是另外一個客戶端而已。
享元
享元模式是池技術的重要實現方式(使用共享對象可有效地支持大量細粒度的對象)。享元模式的目在于運用共享技術,使得一些細粒度的對象可以共享。
享元模式將細粒度的對象信息分為兩部分:內部狀態(intrinsic,對象可共享的信息存儲在享元對象內部且不隨環境而改變),外部狀態(extrinsic,對象依賴的標記隨環境而改變)。
class Flyweight{public: // 享元角色必須接受外部狀態 Flyweight(const string& extrinsic) : mExtrinsic(extrinsic) {} virtual void Operate() = 0; string GetIntrinsic() { return mIntrinsic; } void SetIntrinsic(const string& extrinsic) { mIntrinsic = extrinsic; }protected: const string mExtrinsic; // 外部狀態private: string mIntrinsic; // 內部狀態};class ConcreteFlyweight1 : public Flyweight{public: ConcreteFlyweight1(const string& extrinsic) : Flyweight(extrinsic) {} void Operate() override { cout << "ConcreteFlyweight1: " << mExtrinsic << endl; }};class ConcreteFlyweight2 : public Flyweight{public: ConcreteFlyweight2(const string& extrinsic) : Flyweight(extrinsic) {} void Operate() override { cout << "ConcreteFlyweight2: " << mExtrinsic << endl; }};class FlyweightFactory{public: static shared_ptr說明:
Flyweight 抽象享元角色,定義對象的外部、內部狀態的接口。
ConcreteFlyweight 具體享元角色,實現抽象角色定義的業務。
unsharedConcreteFlyweight 不可共享的享元角色,不存在外部狀態或安全要求(線程安全)不能夠使用共享技術的對象,一般不出現在享元工廠中。
FlyweightFactory 享元工廠,構造一個池容器,同時提供從池中獲得對象的方法。
代理/委托
代理模式為其他對象提供一種代理以控制對這個對象的訪問。
在設計模式中又可分為普通代理和強制代理:
普通代理:客戶端只能訪問代理角色,而不能訪問真實角色
強制代理:從真實角色查找到代理角色,不允許直接訪問真實角色。
class Subject{public: virtual void Request() = 0;};class RealSubject : public Subject{public: void Request() override { cout << "RealSubject is doing something" << endl;}};class Proxy : public Subject{public: Proxy(Subject* subject) : mSubject(subject) {} void Request() override { Before(); mSubject->Request(); After(); }private: void Before() { cout << "preparing ..." << endl; } void After() { cout << "Finishing ..." << endl; } Subject* mSubject;};int main(){ Subject* realSubject = new RealSubject(); Proxy proxy(realSubject); proxy.Request(); return 0;}說明:
Subject 抽象主題角色。
RealSubjuect 具體主題角色,業務邏輯的具體執行者。
Proxy 代理主題角色(委托類、代理類),負責對真實角色的應用。(也可以在真實角色處理完畢前后做預處理和善后處理工作)
行為模式
責任鏈
責任鏈模式使多個對象都有機會處理請求,從而避免了請求的發送者和接受者之間的耦合關系。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,知道有對象處理它為止。
在實際應用中,一般會有一個封裝類對責任模式進行封裝,也就是替代Client類,直接返回鏈中第一個處理者,具體鏈的設置不需要高層次模塊關系。
class Handler{public: virtual ~ Handler() {} void HandleRequest(int32_t requestLevel){ if (GetHandlerLevel() == requestLevel) { DoSomething(); } else { if (mNextHandler) { mNextHandler->HandleRequest(requestLevel); } else { cout << "can not find request handler" << endl; } } } void SetNextHandler(Handler* handler){ mNextHandler = handler; } virtual int32_t GetHandlerLevel() = 0; virtual void DoSomething() = 0;private: Handler* mNextHandler;};class ConcreteHandler1 : public Handler{public: int32_t GetHandlerLevel() override { return 1; } void DoSomething() override { cout << "ConcreteHandler1 is doing something" << endl;}};class ConcreteHandler2 : public Handler{public: int32_t GetHandlerLevel() override { return 2; } void DoSomething() override { cout << "ConcreteHandler2 is doing something" << endl;}};class ConcreteHandler3 : public Handler{public: int32_t GetHandlerLevel() override { return 3; } void DoSomething() override { cout << "ConcreteHandler3 is doing something" << endl;}};int main(){ Handler* handler1 = new ConcreteHandler1(); Handler* handler2 = new ConcreteHandler2(); Handler* handler3 = new ConcreteHandler3(); handler1->SetNextHandler(handler2); handler2->SetNextHandler(handler3); handler1->HandleRequest(4); delete handler1; delete handler2; delete handler3; return 0;}說明:
Handler 抽象處理者
ConcreteHandler 具體的處理者
命令
將一個請求封裝成一個對象,從而讓你使用不同的請求把客戶端參數化,對請求排隊或者請求日志,可以提供撤銷和恢復功能。
class Receiver{public: virtual void DoSomething() = 0;};class ConcreteReceiver1 : public Receiver{public: void DoSomething() override { std::cout << "ConcreteReceiver1 is doing something" << std::endl; }};class ConcreteReceiver2 : public Receiver{public: void DoSomething() override { std::cout << "ConcreteReceiver2 is doing something" << std::endl; }};class Command{public: Command(const std::shared_ptr說明:
Receive接收者角色 該角色就是干活的角色,命令傳遞到這里是應該被執行的。
Command命令角色 需要執行的所有命令都在這里聲明。
Invoker調用者角色 接受命令,并執行命令。
這樣調用者角色(Invoker)與接受者角色(Receiver)之間沒有任何關系,調用者實現功能時只需要調用Command抽象類的execute方法就可以,不需要到底是哪個接受者執行,實現了類間解耦。
這樣Command的子類可以非常容易擴展(可擴展性),調用者Invoker和高層次模塊Client不產生嚴重的代碼耦合。
迭代器
迭代器模式提供一種方法訪問一個容器對象中各個元素,而不需要暴露該對象的內部細節。
迭代器模式目前已經是一個沒落的模式,因為隨著現代編程語言的發展和標準庫的豐富,使用迭代器模式的場景變得越來越少。
在C++中可以使用STL庫提供的迭代器,例如使用vector容器的迭代器來遍歷容器內的元素。此外,C++也支持自定義迭代器,開發者可以根據具體需求實現自己的迭代器。自定義迭代器需要實現相應的迭代器接口,例如begin、end、operator++等,以及重載解引用運算符(*)和箭頭運算符(->)等。
中介者
用一個中介者封裝一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使其耦合松散。
在多個對象依賴的情況下,通過加入中介者角色,取消了多個對象的關聯或依賴關系,減少了對象的耦合性。相對的中介者會膨脹的很大,同事類越多中介者邏輯越復雜。
class Mediator;class Colleague{public: Colleague(const shared_ptr同事類使用構造函數注入中介者,而中介者使用get/set方法注入同事類,是因為同事類必須有中介者,而中介者可以只有部分同事類。
說明:
Mediator抽象中介者角色 定義統一接口,用于各同事角色之間的通信。
Concrete Mediator具體中介者角色 通過協調各同事角色實現協作行為,因此它必須依賴于各個同事角色。
Colleague同事角色 每一個同事角色都知道中介者角色,而且與其他同事角色通信時,一定要通過中介者角色協作。
備忘錄
備忘錄模式在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態。這樣以后就可以將該對象恢復到原先保存的狀態。
使用場景:需要保存和恢復數據的相關狀態場景;需要提供可回滾操作;需要監控的副本場景。
class Memento{public: Memento() {} Memento(string state) :mState(state) {} const string& GetState() { return mState; } void SetState(const string& state) { mState = state; }private: string mState;};class Originator{public: const string& GetState() { return mState; } void SetState(const string& state) { mState = state; } Memento CreateMemento() { return Memento(mState); } void Restore(Memento memento) { SetState(memento.GetState()); }private: string mState;};class Caretaker{public: Memento& GetMemento() { return mMemento; } void SetMemento(Memento memento) { mMemento = memento; }private: Memento mMemento;};int main(){ Originator originator; originator.SetState("state1"); Caretaker caretaker; caretaker.SetMemento(originator.CreateMemento()); originator.SetState("state2"); originator.Restore(caretaker.GetMemento()); cout << "current state: " << originator.GetState() << endl; return 0;}說明:
Originator 發起人角色,記錄當前內部狀態,負責創建恢復備忘錄數據
Memento 備忘錄角色,負責存儲Originator發起人對象的內部狀態,需要時提供發起人需要的內部狀態
Caretaker 備忘錄管理員角色,對備忘錄進行管理、保存和提供備忘錄
觀察者
也稱為發布-訂閱模式。定義對象間的一種一對多的依賴關系,當一個對象(可觀察對象)的狀態發生改變時,所有依賴于它的對象(觀察者)都得到通知并被自動更新。
class Observer {public: virtual void Update(string &context) = 0;};class Observer1 : public Observer {public: void Update(string &context) override { cout << "observer1 get message: " + context << endl; }};class Observer2 : public Observer {public: void Update(string &context) override { cout << "observer2 get message: " + context << endl; }};class Observable {public: virtual void AddObserver(Observer *observer) = 0; virtual void DeleteObserver(Observer *observer) = 0; virtual void NotifyObserver() = 0;};class Subject : public Observable {public: string GetState() { return mContext; }; void SetState(string context) { mContext = context; }; void AddObserver(Observer *observer) override { mObserverList.push_back(observer); }; void DeleteObserver(Observer *observer)override { mObserverList.remove(observer); }; void NotifyObserver() override { for (const auto& it : mObserverList) it->Update(mContext); };private: list輸出:
observer1 get message: I"m doing something
observer2 get message: I"m doing something
擴展:根據應用場景的不同,觀察者模式會對應不同的代碼實現方式:有同步阻塞的實現方式,也有異步非阻塞的實現方式;有進程內的實現方式,也有跨進程的實現方式觀察者模式應用場景[7]。
.
監聽器
也稱為回調模式。它使用回調函數來處理事件,當事件發生時,它會調用預先定義的回調函數來響應事件。這種模式通常用于異步編程,例如事件驅動編程或GUI編程。雖然觀察者模式和監聽器模式具有相似的特性,但它們的目的和實現方式略有不同。觀察者模式通常用于實現對象之間的通信和協作,而監聽器模式則更側重于事件處理和異步編程。
狀態
狀態模式就是當一個對象內在狀態改變時,允許其改變行為,這個對象看起來像改變了其類。
使用場景:行為隨狀態改變而改變的場景;條件、分支判斷語句的替代者。
class State;class Context{public: Context(); void SetState(State* state); State* GetState1(); State* GetState2(); State* GetState(); void Handle1(); void Handle2();private: State* mState1; State* mState2; State* mCurrentState;};class State{public: State(Context* context) : mContext(context) {} virtual void Handle1() = 0; virtual void Handle2() = 0;protected: Context* mContext;};class ConcreteState1 : public State{public: ConcreteState1(Context* context) : State(context) {} void Handle1() override { cout << "ConcreteState1 is doing something" << endl; }; void Handle2() override{ mContext->SetState(mContext->GetState2()); }};class ConcreteState2 : public State{public: ConcreteState2(Context* context) : State(context) {} void Handle1() override { cout << "ConcreteState2 is doing something" << endl; }; void Handle2() override{ mContext->SetState(mContext->GetState1()); }};Context::Context() : mState1(new ConcreteState1(this)), mState2(new ConcreteState2(this)), mCurrentState(mState1) {}void Context::SetState(State* state) { mCurrentState = state; }State* Context::GetState1() { return mState1; }State* Context::GetState2() { return mState2; }State* Context::GetState() { return mCurrentState; }void Context::Handle1() { mCurrentState->Handle1(); }void Context::Handle2() { mCurrentState->Handle2(); }int main(){ Context context; context.Handle1(); // do something context.Handle2(); // switch to state 2 context.Handle1(); // do something context.Handle2(); // switch to state 1 return 0;}說明:
State 抽象狀態角色,負責對象狀態定義,并且封裝環境角色以實現狀態切換
ConcreteState 具體狀態角色,需實現當前狀態下的行為管理以及狀態的轉變
Context 環境角色,負責具體狀態的切換
策略
定義一組算法,將每個算法都封裝起來,并且使它們之間可以互換。
使用場景:多個類只有在算法或行為上有所不同;算法需要自由切換;需要屏蔽算法規則。
class Strategy{public: virtual void DoSomething() = 0;};class ConcreteStrategy1 : public Strategy{public: void DoSomething() override { cout << "ConcreteStrategy1 is doing something" << endl; }};class ConcreteStrategy2 : public Strategy{public: void DoSomething() override { cout << "ConcreteStrategy2 is doing something" << endl; }};class Context{public: Context(Strategy* strategy) : mStrategy(strategy) {} void DoAnything() { mStrategy->DoSomething(); } private: Strategy* mStrategy;};int main(){ Strategy* strategy = new ConcreteStrategy1(); Context context(strategy); context.DoAnything();}說明:
Context 封裝角色,屏蔽高層模塊對策略、算法的直接訪問,封裝可能存在的變化
Strategy 抽象策略角色,策略、算法的抽象
ConcreteStrategy 具體策略角色,實現抽象策略中的操作
模板方法
定義一個操作中的算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重定義該肅反的某些特定步驟。
模板方法模式可以說是繼承思想的一種應用,但并不是完全等同于繼承。
class AbstractClass{public: virtual void DoSomething() = 0; virtual void DoAnything() = 0; void TemplateMethod(){ DoSomething(); DoAnything(); }};class ConcreteClass1 : public AbstractClass{public: void DoSomething() override { cout << "ConcreteClass1 is doing something" << endl; } void DoAnything() override { cout << "ConcreteClass1 is doing anything" << endl; }};class ConcreteClass2 : public AbstractClass{public: void DoSomething() override { cout << "ConcreteClass2 is doing something" << endl; } void DoAnything() override { cout << "ConcreteClass2 is doing anything" << endl; }};int main(){ AbstractClass* class1 = new ConcreteClass1(); AbstractClass* class2 = new ConcreteClass2(); class1->TemplateMethod(); class2->TemplateMethod();}說明:模版方法模式中方法分為兩類
基本方法,由子類實現
模版方法,一個框架實現對基本方法的調度,完成固定的邏輯
訪問者
封裝一下作用于某種數據結構中的各元素的操作,他可以在不改變數據結構的前提下作用于這些元素的新的操作。
使用場景:需要遍歷多個不同的對象,它們有不同的接口(迭代器只能訪問同類/接口)。
訪問者模式是對迭代器模式的擴充,可以遍歷不同的對象,然后執行不同的操作。
class Visitor;class Element {public: virtual void Accept(Visitor& v) = 0;};class ConcreteElementA : public Element {public: void Accept(Visitor& v) override; void OperationA();};class ConcreteElementB : public Element {public: void Accept(Visitor& v) override; void OperationB();};class Visitor {public: virtual void visit(ConcreteElementA& e) = 0; virtual void visit(ConcreteElementB& e) = 0;};class ConcreteVisitor1 : public Visitor {public: void visit(ConcreteElementA& e) override{ cout << "visit "; e.OperationA(); cout << endl; } void visit(ConcreteElementB& e) override{ cout << "visit "; e.OperationB(); cout << endl; }};class ConcreteVisitor2 : public Visitor {public: void visit(ConcreteElementA& e) override{ // 訪問 ConcreteElementA 的另一個操作 cout << "visit "; e.OperationA(); cout << " in another way" << endl; } void visit(ConcreteElementB& e) override{ // 訪問 ConcreteElementB 的另一個操作 cout << "visit "; e.OperationB(); cout << " in another way" << endl; }};void ConcreteElementA::Accept(Visitor& v) { v.visit(*this); }void ConcreteElementA::OperationA() { cout << "ConcreteElementA"; }void ConcreteElementB::Accept(Visitor& v) { v.visit(*this); }void ConcreteElementB::OperationB() { cout << "ConcreteElementB"; }class ObjectStructure{public: static Element* CreateElement(){ if (rand() % 100 > 50) { return new ConcreteElementA(); } else { return new ConcreteElementB(); } }};int main(){ ConcreteVisitor1 visitor1; ConcreteVisitor2 visitor2; for (int i=0; i < 10; i++) { Element* e = ObjectStructure::CreateElement(); e->Accept(visitor1); e->Accept(visitor2); }}說明:
Vistor 抽象訪問者,聲明訪問者可以訪問哪些元素
ConcreteVistor 具體訪問者,影響訪問者訪問到一個類后該干什么
Element 抽象角色,聲明接受那一類訪問者訪問
ConcreteElement 具體元素,實現accept方法
ObjectStructure 結構對象,元素生產者,一般容納在多個不同類、接口的容器(vector、map)
《設計模式之禪》
refactoring-設計模式分類:https://refactoringguru.cn/design-patterns/
看懂UML類圖和時序圖:https://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html#id2
[1]:https://baike.baidu.com/item/單一職責原則/9456515
[2]:https://baike.baidu.com/item/里氏代換原則/3104388?lemmaFrom=lemma_starMap&fromModule=lemma_starMap
[3]https://baike.baidu.com/item/依賴倒置原則/6189149?lemmaFrom=lemma_starMap&fromModule=lemma_starMap
[4]https://baike.baidu.com/item/接口隔離原則/3104602?lemmaFrom=lemma_starMap&fromModule=lemma_starMap
[5]https://baike.baidu.com/item/迪米特法則/2107000?structureClickId=2107000&structureId=294b5d3c8cc7452ad5c9cdba&structureItemId=6a0cd5ee9cda2b44038895ee&lemmaFrom=starMapContent&fromModule=starMap_content
[6]https://baike.baidu.com/item/開閉原則/2828775?structureClickId=2828775&structureId=294b5d3c8cc7452ad5c9cdba&structureItemId=d59481c0283e288f9b31e60f&lemmaFrom=starMapContent&fromModule=starMap_content
[7]https://blog.51cto.com/u_12279910/4217867#_1
阿里云開發者社區,千萬開發者的選擇
阿里云開發者社區,百萬精品技術內容、千節免費系統課程、豐富的體驗場景、活躍的社群活動、行業專家分享交流,歡迎點擊【閱讀原文】加入我們。
標簽: