前言
介绍
设计模式:就是在解决某一类问题场景时,有既定的优秀的代码框架可以直接使用,有以下优点可取:
(1)代码更易于维护,代码的可读性、复用性、可移植性、健壮性更好。
(2)当软件原有需求有变更或者增加新的需求时,合理的设计模式的应用,能够做到软件设计要求的"开-闭原则",即对修改关闭,对扩展开放,使软件原有功能修改,新功能扩充非常灵活。
(3)合理的设计模式的选择,会使软件设计更加模块化,积极的做到软件设计遵循的“高内聚,低耦合”这一根本原则。
设计模式的7大基本原则
设计原则
精简记忆
定义和目的
单一职责原则
一件事 :一个类只应做一件事。
一个类只负责一个职责,避免类的功能过于复杂,提升可读性和维护性。
开闭原则
扩展开放,修改关闭 :新增功能不改代码。
对扩展开放,对修改关闭,通过扩展类的方式实现功能的增强,而避免修改已有代码。
里氏替换原则
子类能替父类 :子类替换父类不出问题。[子类应该扩展父类的行为,而不是破坏父类的行为。]
子类必须能够替代父类而不影响程序的正确性,保证继承的合理性。
依赖倒置原则
依赖抽象 :依赖接口,不依赖具体实现。
高层模块不应该依赖低层模块,二者都应该依赖于抽象,降低模块之间的耦合。
接口隔离原则
小接口 :多个小接口,避免大而全接口。
使用多个小接口,而不是一个大接口,避免让实现类依赖不需要的接口方法。
迪米特法则
少知道 :少与其他对象打交道。
一个对象应该对其他对象有尽可能少的了解,减少对象之间的耦合,提升系统灵活性。
合成复用原则
优先组合 :用组合代替继承来复用代码。
尽量使用组合而不是继承来实现代码复用,避免继承带来的强耦合问题。
解释:
里氏替换原则(Liskov Substitution Principle,LSP)
定义 :所有引用基类的地方必须能够透明地使用其子类的对象。
解释 :子类对象应该能够替换父类对象而不影响程序的正确性。也就是说,子类在继承父类时,不能破坏父类原有的功能和行为。
好处 :确保子类能够完善地替代父类,增强系统的灵活性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> class Bird {public : virtual void fly () { std::cout << "Bird is flying" << std::endl; } }; class Ostrich : public Bird {public : void fly () override { std::cout << "Ostrich cannot fly" << std::endl; } };
依赖倒置原则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> class LaserPrinter {public : void print () { std::cout << "Printing using a Laser Printer." << std::endl; } }; class Printer {public : void printDocument () { LaserPrinter laserPrinter; laserPrinter.print (); } }; int main () { Printer printer; printer.printDocument (); return 0 ; }
在这个例子中,Printer
类直接依赖于 LaserPrinter
类。如果我们以后想更换为其他类型的打印机(例如喷墨打印机),我们就必须修改 Printer
类,这违反了依赖倒置原则。
单例模式
单例模式顾名思义,保证一个类仅可以有一个实例化对象,并且提供一个可以访问它的全局接口。这是一个创建性模式(主要是指对象的创建方式)。单例模式和多线程结合到是很紧密的。包括两种单例模式,以及线程安全的问题 。
应用
第一种:日志模块
假如 这里有一个类,提供了很多方法来封装了一个日志模块。一个软件本身可能有很多的功能模块,那么这么多的模块都要通过日志模块进行写入一些软件运行的日志信息到磁盘内。应该是把这些所有的日志信息都给到一个日志模块的对象上。
第二种:数据库模块
作为一个个的数据库客户端,是要通过数据库模块与数据库服务器进行交互通信的。那么这个数据库client就可以设计成一个单例:应用软件的其他功能模块如果要与数据库服务器进行交互通信,可以调用一个数据库模块对象的某一个方法即可,不需要去创建那么多的对象。毕竟数据库的请求那么多,不可能做到 每一次处理请求都生成一个数据库对象,这样会导致数据库模块对象特别特别多和数据库的连接也特别的多,占用大量内存且非常麻烦。
实现单例模式必须注意一下几点:
(1)单例类只能有一个实例化对象。
(2)单例类必须自己提供一个实例化对象。
(3)单例类必须提供一个可以访问唯一实例化对象的接口。
(4)单例模式分为懒汉和饿汉两种实现方式。
单例:
饿汉式 对象在程序执行时优先创建【线程安全】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <stdio.h> #include <iostream> using namespace std;class Singleton { public : static Singleton *getInstance () { return &instance; } Singleton (const Singleton &) = delete ; Singleton &operator =(const Singleton &) = delete ; private : static Singleton instance; Singleton () {} }; Singleton Singleton::instance; int main () { Singleton *p1 = Singleton::getInstance (); Singleton *p2 = Singleton::getInstance (); cout << p1 << " " << p2 << endl; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <stdio.h> #include <iostream> using namespace std;class Singleton { public : static Singleton *getInstance () { return &instance; } private : static Singleton instance; Singleton () {} }; Singleton Singleton::instance; int main () { Singleton *p1 = Singleton::getInstance (); Singleton *p2 = Singleton::getInstance (); cout << p1 << " " << p2 << endl; Singleton p = *p1; cout << &p << endl; return 0 ; }
懒汉式: 对象的创建在第一次调用getInstance函数时创建【线程不安全】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <stdio.h> #include <iostream> using namespace std;class Singleton { public : static Singleton *getInstance () { if (instance == nullptr ) { instance = new Singleton (); } return instance; } Singleton (const Singleton &) = delete ; Singleton &operator =(const Singleton &) = delete ; private : static Singleton *instance; Singleton () {} }; Singleton *Singleton::instance = nullptr ; int main () { Singleton *p1 = Singleton::getInstance (); Singleton *p2 = Singleton::getInstance (); cout << p1 << " " << p2 << endl; return 0 ; }
上述懒汉式如何改成线程安全的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <stdio.h> #include <iostream> #include <mutex> using namespace std;class Singleton { public : static Singleton *getInstance () { if (instance == nullptr ) { unique_lock <mutex>(mtx); if (instance == nullptr ) { instance = new Singleton (); } } return instance; } Singleton (const Singleton &) = delete ; Singleton &operator =(const Singleton &) = delete ; private : static Singleton *instance; mutex mtx; Singleton () {} }; Singleton *Singleton::instance = nullptr ; int main () { Singleton *p1 = Singleton::getInstance (); Singleton *p2 = Singleton::getInstance (); cout << p1 << " " << p2 << endl; return 0 ; }
单例模式缺点
全局状态 :
缺点 :单例模式会在应用程序中引入全局状态,这可能导致代码的耦合度增加,使得代码更难以测试和维护。
举例 :假设你有一个 Logger
单例类,它在整个应用程序中记录日志。如果其他类直接依赖于这个单例类,那么在单元测试中模拟或隔离这个依赖就会变得困难。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Logger {private: Logger() {} static Logger* instance; public: static Logger* getInstance () { if (instance == nullptr) { instance = new Logger(); } return instance; } void log (const std ::string & message) { std ::cout << message << std ::endl ; } }; Logger* Logger::instance = nullptr; void someFunction () { Logger::getInstance()->log ("Logging from someFunction" ); }
在这个例子中,Logger
类是一个单例类。someFunction
函数直接依赖于 Logger
单例类,这使得在单元测试中难以隔离 Logger
类。
难以扩展 :
缺点 :由于单例模式限制了类的实例数量,因此很难扩展或修改类的行为。
举例 :假设你有一个 Configuration
单例类,它存储应用程序的配置信息。如果将来需要支持多个配置实例,那么修改代码将会非常困难。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Configuration {private: Configuration() {} static Configuration* instance; public: static Configuration* getInstance () { if (instance == nullptr) { instance = new Configuration(); } return instance; } void setConfig (const std ::string & key, const std ::string & value) { } std ::string getConfig (const std ::string & key) { return "some_config_value" ; } }; Configuration* Configuration::instance = nullptr; void someFunction () { Configuration::getInstance()->setConfig("key" , "value" ); std ::string value = Configuration::getInstance()->getConfig("key" ); std ::cout << value << std ::endl ; }
在这个例子中,Configuration
类是一个单例类。如果将来需要支持多个配置实例,那么修改代码将会非常困难。
线程安全问题 :
缺点 :在多线程环境中,单例模式的实现可能会导致线程安全问题。
举例 :在多线程环境中,如果没有适当的同步机制,多个线程可能会同时创建单例类的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Singleton {private: Singleton() {} static Singleton* instance; public: static Singleton* getInstance () { if (instance == nullptr) { instance = new Singleton(); } return instance; } }; Singleton* Singleton::instance = nullptr;
在这个例子中,Singleton
类的 getInstance
方法不是线程安全的。在多线程环境中,多个线程可能会同时创建 Singleton
类的实例。
为了解决这个问题,可以使用双重检查锁定模式(Double-Checked Locking Pattern)或其他同步机制来确保线程安全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <mutex> class Singleton {private: Singleton() {} static Singleton* instance; static std ::mutex mtx; public: static Singleton* getInstance () { if (instance == nullptr) { std ::lock_guard<std ::mutex> lock (mtx) ; if (instance == nullptr) { instance = new Singleton(); } } return instance; } }; Singleton* Singleton::instance = nullptr; std ::mutex Singleton::mtx;
在这个例子中,使用 std::mutex
来确保 getInstance
方法是线程安全的。
简单工厂
简单工厂:它不属于标准的OOP设计模式中,而后面两种是包含在标准的OOP的23种设计模式中的。
为什么要工厂模式:主要是封装了对象的创建过程。 创建性模式本身就是体现了:对象的创建过程的封装和隐藏。没有工厂模式的封装就是:对象的new 和 new等。当代码里面出现很多的类,每次创建在对象的时候,都需要通过new 类名称的方式来生成对象。
先来看个代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <iostream> #include <string> class Car { public : Car (std::string name):_name(name){} virtual void show () = 0 ; protected : std::string _name; }; class BMW :public Car{ public : BMW (std::string name):Car (name){} void show () { std::cout << "宝马 " << _name << std::endl; } }; class Audi :public Car{ public : Audi (std::string name) :Car (name) {} void show () { std::cout << "奥迪 " << _name << std::endl; } }; int main () { Car* p1 = new BMW ("x1" ); Car* p2 = new Audi ("A6" ); p1->show (); p2->show (); delete p1; delete p2; return 0 ; }
使用简单工厂
解决办法为:把这些对象都给 封装到一个简单的工厂里面。如下:
工厂方法
通过下面这个简单的工厂把所有对象的创建给封装起来了,下面造汽车劳资不用管,我只是想要一辆车,通过传入不同的参数,得到不同的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <iostream> #include <string> class Car { public : Car (std::string name) : _name(name) {} virtual void show () = 0 ; protected : std::string _name; }; class Bmw : public Car{ public : Bmw (std::string name) : Car (name) {} void show () { std::cout << "宝马 " << _name << std::endl; } }; class Audi : public Car{ public : Audi (std::string name) : Car (name) {} void show () { std::cout << "奥迪 " << _name << std::endl; } }; enum CarType { BMW, AUDI }; class SimpleFactory { public : Car *createCar (CarType type) { switch (type) { case BMW: return new Bmw ("x1" ); case AUDI: return new Audi ("A6" ); default : break ; } return nullptr ; } }; int main () { SimpleFactory *factory = new SimpleFactory (); Car *car1 = factory->createCar (BMW); Car *car2 = factory->createCar (AUDI); car1->show (); car2->show (); delete factory; delete car1; delete car2; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <stdio.h> #include <iostream> #include <mutex> #include <string> using namespace std;class F {public : F (string name):name_ (name){} virtual void show () =0 ; string name_; }; class A :public F{public : A (string name):F (name){} void show () { cout<<"AA:" <<name_; } }; class B :public F{public : B (string name):F (name){} void show () { cout<<"BB:" <<name_; } }; enum Type { AA,BB }; class SimpleFactory {public : F * create (Type type) { switch (type) { case AA: return new A ("aaa" ); case BB: return new B ("bbb" ); default : break ; } return nullptr ; } }; signed main () { SimpleFactory simpleFactory ; simpleFactory.create (AA)->show (); return 0 ; }
使用智能指针管理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <iostream> #include <string> #include <memory> using namespace std;class Car { public : Car (std::string name) : _name(name) {} virtual void show () = 0 ; protected : std::string _name; }; class Bmw : public Car{ public : Bmw (std::string name) : Car (name) {} void show () { std::cout << "宝马 " << _name << std::endl; } }; class Audi : public Car{ public : Audi (std::string name) : Car (name) {} void show () { std::cout << "奥迪 " << _name << std::endl; } }; enum CarType { BMW, AUDI }; class SimpleFactory { public : Car *createCar (CarType type) { switch (type) { case BMW: return new Bmw ("x1" ); case AUDI: return new Audi ("A6" ); default : break ; } return nullptr ; } }; int main () { std::unique_ptr<SimpleFactory> factory (new SimpleFactory()) ; std::unique_ptr<Car> car1 (factory->createCar(BMW)) ; std::unique_ptr<Car> car2 (factory->createCar(AUDI)) ; car1->show (); car2->show (); return 0 ; }
简单工厂的缺点:
同一个工厂既建宝马,也整奥迪。不可能用一个工厂把所有对象的创建都封装起来。而且工厂里面的设计也不符合 开闭原则 。
开闭原则
开闭原则是面向对象设计的一种重要原则,它的全称是“对扩展开放,对修改关闭”。这个原则的核心思想是,当应用的需求改变时,我们应该尽量通过添加新的代码进行扩展,而不是去修改已有的代码。
具体来说,开闭原则包含两个方面:
对扩展开放:意味着我们应该设计出可以容易添加新功能的系统,只需要添加新的代码,而不需要修改原有的代码。
对修改关闭:意味着一旦我们完成了系统的设计和编码,应该尽量避免对已有的代码进行修改。因为修改已有的代码会带来很多风险,可能会引入新的错误。
遵守开闭原则的好处是,可以使得系统更加稳定,更具有弹性,更容易进行维护和扩展。但同时,实现开闭原则也需要一定的设计和编码技巧,可能会增加系统的设计和实现的复杂性。
因此,需要根据上述根据对修改关闭,对扩展开放 的原则进行修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <iostream> #include <string> #include <memory> using namespace std;class Car { public : Car (std::string name) : _name(name) {} virtual void show () = 0 ; protected : std::string _name; }; class Bmw : public Car{ public : Bmw (std::string name) : Car (name) {} void show () { std::cout << "宝马 " << _name << std::endl; } }; class Audi : public Car{ public : Audi (std::string name) : Car (name) {} void show () { std::cout << "奥迪 " << _name << std::endl; } }; enum CarType { BMW, AUDI }; class SimpleFactory { public : virtual Car *createCar (string name) = 0 ; }; class BmwFactoryMethod : public SimpleFactory{ Car *createCar (string name) { return new Bmw (name); } }; class AUDIFactoryMethod : public SimpleFactory{ Car *createCar (string name) { return new Audi (name); } }; int main () { std::unique_ptr<SimpleFactory> Afactory (new AUDIFactoryMethod()) ; std::unique_ptr<SimpleFactory> Bfactory (new BmwFactoryMethod()) ; std::unique_ptr<Car> car1 (Afactory->createCar("x1" )) ; std::unique_ptr<Car> car2 (Bfactory->createCar("A5" )) ; car1->show (); car2->show (); return 0 ; }
一种写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <stdio.h> #include <iostream> #include <mutex> #include <string> #include <memory> using namespace std;class F {public : F (string name):name_ (name){} virtual void show () =0 ; string name_; }; class A :public F{public : A (string name):F (name){} void show () { cout<<"AA:" <<name_; } }; class B :public F{public : B (string name):F (name){} void show () { cout<<"BB:" <<name_; } }; class SimpleFactory {public : virtual F* create (string name) =0 ; }; class AFun :public SimpleFactory{public : F* create (string name) { return new A (name); } }; signed main () { unique_ptr<SimpleFactory> nna (new AFun()) ; unique_ptr<F> aaaa (nna->create("aa" )) ; aaaa->show (); return 0 ; }
把工厂划分为一个继承结构:封装了一个工厂方法(纯虚函数)。派生类代表具体的工厂,产生具体的产品。(相应的工厂创建相应的产品),达到了一个工厂其相应的产品。
这就是对已有的功能进行封闭。开闭原则:对修改关闭,对扩展开放 此时要是删除一种产品的工厂,直接删除相应的派生类即可(也不会改动其他的类)。在调用的时候,想要获取一个对象,只需要调用 工厂相应的工厂方法就可以了,不需要去了解派生类叫什么名字、也不需要知道对象是怎么创建的(这些细节由工厂进行维护)。
抽象工厂
简单工厂的缺点如下:
比如说是 一类有关联关系的产品:手机、手机上的耳机等。根据上面的工厂方法,创建耳机,也得整上一个相应的耳机工厂,这样做 就有些不太现实(具体的一个个工厂类就太多了)。也就是这些一系列有关联关系的产品 应该放在一个工厂进行创建的。 毕竟有关联关系的产品太多了,不可能各个产品都创建相对应的工厂(工厂类将会极为庞大)。
解决方法:将工厂方法升级为抽象工厂。抽象工厂也是需要工厂方法的,抽象工厂:对一系列有关联关系的产品簇提供产品对象的统一创建。
就可以把一组有关联关系的产品簇提供产品对象的统一创建 放到一个工厂里面做了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 #include <iostream> #include <string> #include <memory> class Car { public : Car (std::string name) : _name(name) {} virtual void show () = 0 ; protected : std::string _name; }; class Bmw : public Car{ public : Bmw (std::string name) : Car (name) {} void show () { std::cout << "宝马 " << _name << std::endl; } }; class Audi : public Car{ public : Audi (std::string name) : Car (name) {} void show () { std::cout << "奥迪 " << _name << std::endl; } }; class CarLight { public : virtual void light () = 0 ; }; class BmwLight : public CarLight{ public : void light () { std::cout << "宝马车灯 " << std::endl; } }; class AudiLight : public CarLight{ public : void light () { std::cout << "奥迪车灯 " << std::endl; } }; class AbstractFactory { public : virtual Car *createOneCar (std::string) = 0 ; virtual CarLight *createCarLight () = 0 ; }; class BMWAbstractFactory : public AbstractFactory{ Car *createOneCar (std::string name) { return new Bmw (name); } CarLight *createCarLight () { return new BmwLight (); } }; class AUDIAbstractFactory : public AbstractFactory{ Car *createOneCar (std::string name) { return new Audi (name); } CarLight *createCarLight () { return new AudiLight (); } }; int main () { std::unique_ptr<AbstractFactory> bmwAbstractFactory (new BMWAbstractFactory()) ; std::unique_ptr<AbstractFactory> audiAbstractFactory (new AUDIAbstractFactory()) ; std::unique_ptr<Car> p1 (bmwAbstractFactory->createOneCar("x1" )) ; std::unique_ptr<Car> p2 (audiAbstractFactory->createOneCar("A5" )) ; std::unique_ptr<CarLight> p3 (bmwAbstractFactory->createCarLight()) ; std::unique_ptr<CarLight> p4 (audiAbstractFactory->createCarLight()) ; p1->show (); p2->show (); p3->light (); p4->light (); return 0 ; }
代理模式
结构型模式:(不是关注于对象的产生,关注于最后通过类与类的组合之后,功能上该怎么使用?以及对问题场景的符合与否?)
这些设计模式关注于类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
代理模式:Proxy Pattern 。主要体现的是 对象访问权限的控制。这个Proxy代理可以把那些访问对象的权限不够的用户 都给挡回去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 #include <iostream> #include <string> #include <memory> class VedioWebSite { public : virtual void freeMovie () = 0 ; virtual void vipMovie () = 0 ; virtual void ticketMovie () = 0 ; }; class OperatorMovieBoss : public VedioWebSite { public : virtual void freeMovie () { std::cout << "你可以看免费电影" << std::endl; } virtual void vipMovie () { std::cout << "你可以看VIP电影" << std::endl; } virtual void ticketMovie () { std::cout << "你可以看VIP + 券电影" << std::endl; } }; class FreeMovieProxy : public VedioWebSite { public : FreeMovieProxy () { pvedio = new OperatorMovieBoss (); } ~FreeMovieProxy () { delete pvedio; } virtual void freeMovie () { pvedio->freeMovie (); } virtual void vipMovie () { std::cout << "你只是普通用户,不可以看VIP电影,请升级为会员" << std::endl; } virtual void ticketMovie () { std::cout << "你没有电源券,不可以观看电影,请先升级为会员然后购买影券" << std::endl; } private : VedioWebSite *pvedio; }; class VIPMovieProxy : public VedioWebSite { public : VIPMovieProxy () { pvedio = new OperatorMovieBoss (); } ~VIPMovieProxy () { delete pvedio; } virtual void freeMovie () { pvedio->freeMovie (); } virtual void vipMovie () { pvedio->vipMovie (); } virtual void ticketMovie () { std::cout << "你没有电源券,不可以观看电影,请先购买影券" << std::endl; } private : VedioWebSite *pvedio; }; void WhoWatchMovie (std::unique_ptr<VedioWebSite> &ptr) { ptr->freeMovie (); ptr->vipMovie (); ptr->ticketMovie (); } int main () { std::unique_ptr<VedioWebSite> p1 (new FreeMovieProxy()) ; WhoWatchMovie (p1); std::cout << "*****************************" << std::endl; std::unique_ptr<VedioWebSite> p2 (new VIPMovieProxy()) ; WhoWatchMovie (p2); return 0 ; }
总结:代理模式涉及到了 抽象类(公共类),委托类(需要从抽象类继承而来),代理类(需要从抽象类继承而来),以组合的方式 使用代理对象,在实际的使用中客户直接访问的是代理对象。
应用场景
远程代理
Spring Cloud Feign 使用远程代理机制。
举例说明:
我们假设有两个微服务:User Service 和 Order Service 。其中,Order Service
需要调用 User Service
来获取用户信息。
1. 定义远程服务接口 (Feign Client)
首先,在 Order Service
中定义一个接口用于访问 User Service
。这个接口就是通过 Feign 来实现的远程代理。
1 2 3 4 5 6 @FeignClient(name = "user-service") public interface UserClient { @GetMapping("/users/{id}") User getUserById (@PathVariable("id") Long id) ; }
@FeignClient :用于声明这是一个 Feign 客户端,name
表示要调用的服务名称,在服务注册中心(如 Eureka)中,user-service
是服务的名字。
@GetMapping :定义了远程服务的 HTTP 端点,这里表示调用 User Service
的 /users/{id}
接口。
2. 在本地使用 Feign 客户端
在 Order Service
中,我们可以通过自动注入 UserClient
来调用远程的 User Service
,就像调用本地方法一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @RestController @RequestMapping("/orders") public class OrderController { @Autowired private UserClient userClient; @GetMapping("/{orderId}") public Order getOrder (@PathVariable Long orderId) { User user = userClient.getUserById(1L ); Order order = new Order (orderId, "Product A" , 1L , user); return order; } }
在这段代码中,userClient.getUserById(1L)
看起来像是调用了一个普通的本地方法,实际上它是通过 Feign 动态代理调用了远程的 User Service
服务的 /users/{id}
端点。
3. Feign 的工作原理
动态代理 :当 userClient.getUserById(1L)
被调用时,Feign 会生成一个代理对象来拦截这个方法调用,代理对象会将这个方法调用转化为一个HTTP 请求 ,向 User Service
发送请求。
请求封装 :Feign 根据 @GetMapping("/users/{id}")
注解,知道要发送一个 GET
请求到 /users/1
,并将响应映射为 User
对象。
响应处理 :User Service
返回的 HTTP 响应会被 Feign 解析,并自动转换为 User
对象返回给调用方。
动态代理
这里大致说下AOP-动态代理的实现
JDK 动态代理实现 Spring AOP 的底层原理
JDK 动态代理的原理
JDK 动态代理要求目标类必须实现一个或多个接口。通过 Proxy
类生成代理对象,代理对象实现了目标类的所有接口,并在代理对象内部,通过 InvocationHandler
接口实现对方法调用的拦截。
1.定义业务接口和实现类
假设我们有一个简单的业务接口 UserService
,其中包含 addUser
方法。
1 2 3 4 5 6 7 8 9 10 11 12 public interface UserService { void addUser (String name) ; } public class UserServiceImpl implements UserService { @Override public void addUser (String name) { System.out.println("Adding user: " + name); } }
2.使用 JDK 动态代理实现 AOP 功能
我们通过实现 InvocationHandler
接口来拦截对 UserService
的方法调用,并在方法执行前后插入切面逻辑(比如日志记录)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class AOPInvocationHandler implements InvocationHandler { private Object target; public AOPInvocationHandler (Object target) { this .target = target; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method: " + method.getName()); Object result = method.invoke(target, args); System.out.println("After method: " + method.getName()); return result; } public static Object createProxy (Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new AOPInvocationHandler (target) ); } }
3.测试动态代理的 AOP 功能
我们通过 AOPInvocationHandler
为 UserService
创建代理对象,并通过代理对象调用方法,观察 AOP 的效果。
1 2 3 4 5 6 7 8 9 10 11 12 public class Main { public static void main (String[] args) { UserService userService = new UserServiceImpl (); UserService proxy = (UserService) AOPInvocationHandler.createProxy(userService); proxy.addUser("John Doe" ); } }
日志代理
日志代理是一种常见的代理模式 应用场景。代理模式的目的是在不修改原有对象的基础上,通过代理对象来控制对真实对象的访问或在访问之前和之后添加额外的功能。在日志代理中,代理对象会记录对原始对象的调用日志,从而方便对操作进行记录和管理。
场景:
假设我们有一个简单的银行账户类 BankAccount
,它有存款和取款功能。现在我们希望在每次存款和取款时记录操作日志,便于调试和审计。我们可以通过代理模式来实现日志记录功能,而不需要修改 BankAccount
类的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <iostream> #include <string> class IBankAccount {public : virtual void Deposit (int amount) = 0 ; virtual void Withdraw (int amount) = 0 ; virtual ~IBankAccount () = default ; }; class BankAccount : public IBankAccount {private : int balance; public : BankAccount () : balance (0 ) {} void Deposit (int amount) override { balance += amount; std::cout << "Deposited " << amount << ", current balance: " << balance << std::endl; } void Withdraw (int amount) override { if (balance >= amount) { balance -= amount; std::cout << "Withdrew " << amount << ", current balance: " << balance << std::endl; } else { std::cout << "Insufficient balance. Current balance: " << balance << std::endl; } } }; class BankAccountProxy : public IBankAccount {private : IBankAccount* realAccount; public : BankAccountProxy (IBankAccount* account) : realAccount (account) {} void Deposit (int amount) override { std::cout << "[LOG] Deposit request: " << amount << std::endl; realAccount->Deposit (amount); } void Withdraw (int amount) override { std::cout << "[LOG] Withdraw request: " << amount << std::endl; realAccount->Withdraw (amount); } }; int main () { BankAccount account; BankAccountProxy proxy (&account) ; proxy.Deposit (100 ); proxy.Withdraw (50 ); proxy.Withdraw (100 ); return 0 ; }
输出:
1 2 3 4 5 6 [LOG] Deposit request: 100 Deposited 100, current balance: 100 [LOG] Withdraw request: 50 Withdrew 50, current balance: 50 [LOG] Withdraw request: 100 Insufficient balance. Current balance: 50
缓存代理
缓存代理是一种设计模式,主要用于避免重复计算或频繁访问资源带来的性能开销。通过缓存之前计算或访问的结果,下次请求相同数据时,可以直接从缓存中获取,避免再次计算或访问实际资源。
缓存代理的工作原理 :
缓存代理 首先会去检查缓存中是否已经有所需的数据。
如果缓存中有数据,它会直接返回缓存中的数据。
如果缓存中没有数据,它会去访问实际对象(或执行计算),获取数据后将结果存储到缓存中,方便后续请求使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import java.util.HashMap;import java.util.Map;public class CacheProxy { private Map<Integer, Integer> cache = new HashMap <>(); public int expensiveOperation (int input) { if (cache.containsKey(input)) { System.out.println("从缓存中获取结果..." ); return cache.get(input); } System.out.println("执行复杂的计算..." ); int result = input * input; cache.put(input, result); return result; } public static void main (String[] args) { CacheProxy proxy = new CacheProxy (); System.out.println("结果: " + proxy.expensiveOperation(4 )); System.out.println("结果: " + proxy.expensiveOperation(4 )); System.out.println("结果: " + proxy.expensiveOperation(5 )); } }
装饰器模式
结构型模式的一种:Decorator Pattern
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 #include <iostream> #include <string> #include <memory> using namespace std;class Car { public : virtual void show () = 0 ; }; class Bmw : public Car{ public : void show () { cout << "这是宝马汽车的配置为:" ; } }; class Audi : public Car{ public : void show () { cout << "这是奥迪汽车的配置为:" ; } }; class Bnze : public Car{ public : void show () { cout << "这是奔驰汽车的配置为:" ; } }; class CarBaseDecorator : public Car { public : virtual void show () = 0 ; }; class CarChildDecorator1 : public CarBaseDecorator{ public : CarChildDecorator1 (Car *p) { _ptr = p; } void show () { _ptr->show (); cout << " 定速巡航功能" ; } private : Car *_ptr; }; class CarChildDecorator2 : public CarBaseDecorator{ public : CarChildDecorator2 (Car *p) { _ptr = p; } void show () { _ptr->show (); cout << " 自动驾驶功能" ; } private : Car *_ptr; }; class CarChildDecorator3 : public CarBaseDecorator{ public : CarChildDecorator3 (Car *p) { _ptr = p; } void show () { _ptr->show (); cout << " 车道偏离功能" ; } private : Car *_ptr; }; int main () { Car *p1 = new CarChildDecorator1 (new Bmw ()); p1 = new CarChildDecorator2 (p1); p1 = new CarChildDecorator3 (p1); p1->show (); cout << endl; cout << "************************************" << endl; Car *p2 = new CarChildDecorator2 (new Audi ()); p2->show (); cout << endl; cout << "************************************" << endl; Car *p3 = new CarChildDecorator3 (new Bnze ()); p3->show (); cout << endl; return 0 ; }
从深入理解对象模型的角度看有点套娃的味了,应用了虚函数指针的运行时多态实现。
上述代码详细解释:
在这个代码中,我们使用了装饰器模式,也称为包装器模式。装饰器模式允许我们在运行时动态地添加行为或状态到现有的对象中,而不需要修改其原始类的源代码。
当我们执行 p1->show();
时,输出的结果是 “这是宝马汽车的配置为: 定速巡航功能 自动驾驶功能 车道偏离功能”,这是因为我们在创建 p1
时,逐步地向它添加了这些功能:
Car *p1 = new CarChildDecorator1(new Bmw());
在这一步,我们创建了一个 Bmw
对象,并将其作为参数传递给 CarChildDecorator1
的构造函数。这个操作把 “定速巡航功能” 添加到了 Bmw
对象中。
p1 = new CarChildDecorator2(p1);
在这一步,我们创建了一个 CarChildDecorator2
对象,并将已经包含了 “定速巡航功能” 的 p1
作为参数传递给 CarChildDecorator2
的构造函数。这个操作把 “自动驾驶功能” 添加到了 p1
中。
p1 = new CarChildDecorator3(p1);
在这一步,我们创建了一个 CarChildDecorator3
对象,并将已经包含了 “定速巡航功能” 和 “自动驾驶功能” 的 p1
作为参数传递给 CarChildDecorator3
的构造函数。这个操作把 “车道偏离功能” 添加到了 p1
中。
因此,当我们调用 p1->show();
时,p1
中的每个装饰器都会调用其内部存储的 Car
对象的 show
方法,并在此基础上添加自己的功能描述,从而得到了 “这是宝马汽车的配置为: 定速巡航功能 自动驾驶功能 车道偏离功能” 这个结果。
这就是装饰器模式的魅力,它允许我们动态地添加和组合功能,而不需要修改原始类的代码 。
适配器模式
这个适配器有点意思。
在项目中 使用到第三方的插件或者库,但是因为接口不兼容 就需要去添加很多的适配器类。
例如场景:电脑使用一个接口 ,把桌面演示投影到 投影仪上。
假如:现在有三种类型的接口:VGA HDMI TypeC 。但是目前电脑就用VGA,那么我们就需要用适配器将HDMI转成VGA的格式。把它想成显卡适配器就好理解了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <iostream> #include <string> #include <memory> using namespace std;class VGA { public : virtual void play () = 0 ; }; class TV01 : public VGA{ public : void play () { cout << "通过VGA接口连接投影仪,进行视频播放" << endl; } }; class Computer { public : void playVideo (VGA *pVGA) { pVGA->play (); } }; class HDMI { public : virtual void play () = 0 ; }; class TV02 : public HDMI{ public : void play () { cout << "通过HDMI接口连接投影仪,进行视频播放" << endl; } }; class VGAToHDMIAdapter : public VGA{ public : VGAToHDMIAdapter (HDMI *p) : pHdmi (p) {} void play () { pHdmi->play (); } private : HDMI *pHdmi; }; int main () { Computer computer; computer.playVideo (new VGAToHDMIAdapter (new TV02 ())); return 0 ; }
观察者模式
观察者-监听者模式(发布订阅模式),隶属于行为型模式。
行为型模式:主要关注的是对象之间的通信,例如:对象A调用对象B的成员方法等。
观察者-监听者模式:主要处理 对象的一对多的关系,也就是多个对象都依赖于一个对象。当该对象的状态发生改变时,其他对象都可以接受到相应的通知 。假如现在一个类表示了一组数据,生成一个对象。通过这同一个数据对象, 可以得到一组 圆饼图、柱状图、条形图等。
简单解释:
这段代码实现的是发布-订阅模式,也称为观察者模式。这是一种在对象之间定义一对多的依赖关系,当一个对象状态改变时,所有依赖于它的对象都会受到通知并被自动更新。
在这个代码中,Observer
是观察者的抽象基类,Observer1
、Observer2
和 Observer3
都是具体的观察者,他们分别对1、2、3号的消息感兴趣。Subject
是主题类,用来存储每个观察者对哪个消息感兴趣。
当一个消息发生改变时,主题类 Subject
会通过 dispatch
方法找到对这个消息感兴趣的所有观察者,然后调用它们的 upData
方法,通知它们消息已经发生改变。这就是观察者模式的核心。
例如,如果1号消息发生改变,主题类就会找到对1号消息感兴趣的 Observer1
和 Observer2
,然后通知他们。如果2号消息发生改变,主题类就会找到对2号消息感兴趣的 Observer1
和 Observer3
,然后通知他们。如果3号消息发生改变,主题类就会找到对3号消息感兴趣的 Observer2
和 Observer3
,然后通知他们。
这种模式在很多实际应用中都非常有用,比如在GUI编程中,一个按钮的点击事件就可以看作是一个主题,而对这个点击事件感兴趣的所有监听器就是观察者。当用户点击按钮时,所有的监听器都会收到通知。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 #include <iostream> #include <string> #include <unordered_map> #include <list> using namespace std;class Observer { public : virtual void upData (int messageid) = 0 ; }; class Observer1 : public Observer{ public : void upData (int id) { switch (id) { case 1 : cout << "观察者1 对1 和 2号消息感兴趣.1号消息发生改变" << endl; break ; case 2 : cout << "观察者1 对1 和 2号消息感兴趣.2号消息发生改变" << endl; break ; } } }; class Observer2 : public Observer{ public : void upData (int id) { switch (id) { case 1 : cout << "观察者2 对1 和 3号消息感兴趣.1号消息发生改变" << endl; break ; case 3 : cout << "观察者2 对1 和 3号消息感兴趣.3号消息发生改变" << endl; break ; } } }; class Observer3 : public Observer{ public : void upData (int id) { switch (id) { case 2 : cout << "观察者3 对2 和 3号消息感兴趣.2号消息发生改变" << endl; break ; case 3 : cout << "观察者3 对2 和 3号消息感兴趣.3号消息发生改变" << endl; break ; } } }; class Subject { public : void addObserver (Observer *obser, int messageid) { unordered_map<int , list<Observer *>>::iterator it = mapping.find (messageid); if (it != mapping.end ()) { it->second.push_back (obser); } else { list<Observer *> mylist; mylist.push_back (obser); mapping.insert ({messageid, mylist}); } } void dispatch (int messageid) { auto it = mapping.find (messageid); if (it != mapping.end ()) { for (Observer *pObserver : it->second) { pObserver->upData (messageid); } } } private : unordered_map<int , list<Observer *>> mapping; }; int main () { Subject subject; subject.addObserver (new Observer1 (), 1 ); subject.addObserver (new Observer1 (), 2 ); subject.addObserver (new Observer2 (), 1 ); subject.addObserver (new Observer2 (), 3 ); subject.addObserver (new Observer3 (), 2 ); subject.addObserver (new Observer3 (), 3 ); cout << "************************************" << endl; int changeMessid; cout << "请输入一个发生改变的消息 输入-1结束" << endl; cout << "哪个消息改变了:" ; while (cin >> changeMessid && changeMessid != -1 ) { subject.dispatch (changeMessid); cout << "哪个消息改变了:" ; } return 0 ; }
理解成发布-订阅模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <iostream> #include <vector> #include <string> #include <algorithm> class Observer { public : virtual void update (const std::string& message) = 0 ; virtual ~Observer () {} }; class StockTicker { std::vector<Observer*> observers; public : void attach (Observer* obs) { observers.push_back (obs); } void detach (Observer* obs) { observers.erase (std::remove (observers.begin (), observers.end (), obs), observers.end ()); } void notify (const std::string& message) { for (Observer* obs : observers) { obs->update (message); } } void newPriceUpdate (const std::string& stock, double price) { notify ("Price updated - " + stock + ": $" + std::to_string (price)); } }; class Investor : public Observer { std::string name; public : Investor (const std::string& name) : name (name) {} void update (const std::string& message) override { std::cout << name << " received: " << message << std::endl; } }; int main () { StockTicker ticker; Investor alice ("Alice" ) ; Investor bob ("Bob" ) ; ticker.attach (&alice); ticker.attach (&bob); ticker.newPriceUpdate ("AAPL" , 150.50 ); ticker.newPriceUpdate ("GOOGL" , 2729.34 ); ticker.detach (&alice); ticker.newPriceUpdate ("AAPL" , 151.45 ); return 0 ; }
应用场景
发布-订阅系统
事件处理机制
实时数据更新
消息队列系统
享元模式
享元模式是一种结构型设计模式 ,它通过共享细粒度 对象来减少内存使用,以提高性能。该模式主要用于处理大量相似对象 的情况,通过共享相同的对象,避免重复创建相似对象,从而节省内存。
在享元模式中,通常会把对象的状态分为两部分:
内部状态 :可以共享的状态,不会随外部环境变化,保存在享元对象内部。
外部状态 :不能共享的状态,会随外部环境变化,需要在对象方法调用时动态传递。
示例场景:字符显示系统
假设你正在开发一个文本编辑器,每个字符都需要保存并显示在屏幕上。如果直接为每个字符创建对象,将占用大量内存。通过享元模式,我们可以共享相同的字符对象。
实现步骤
Flyweight(享元接口): 定义一个方法用于显示字符,同时接收外部状态(即字符的位置)。
ConcreteFlyweight(具体享元类): 实现 Flyweight 接口,包含可以共享的内部状态(如字符的字体、颜色等)。
FlyweightFactory(享元工厂): 维护一个享元对象的共享池,负责返回已存在的共享对象,或者创建一个新对象并放入池中。
Client(客户端): 客户端负责管理外部状态,并在需要时调用享元对象的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include <iostream> #include <unordered_map> #include <string> #include <memory> class Character {public : virtual void Display (int x, int y) = 0 ; virtual ~Character () = default ; }; class ConcreteCharacter : public Character {private : char symbol; public : ConcreteCharacter (char sym) : symbol (sym) {} void Display (int x, int y) override { std::cout << "Character: " << symbol << " at position (" << x << ", " << y << ")\n" ; } }; class CharacterFactory {private : std::unordered_map<char , std::shared_ptr<Character>> characters; public : std::shared_ptr<Character> GetCharacter (char symbol) { if (characters.find (symbol) == characters.end ()) { characters[symbol] = std::make_shared <ConcreteCharacter>(symbol); } return characters[symbol]; } }; int main () { CharacterFactory factory; std::shared_ptr<Character> charA1 = factory.GetCharacter ('A' ); charA1->Display (10 , 20 ); std::shared_ptr<Character> charA2 = factory.GetCharacter ('A' ); charA2->Display (30 , 40 ); std::shared_ptr<Character> charB = factory.GetCharacter ('B' ); charB->Display (50 , 60 ); std::cout << "charA1 and charA2 are the same object: " << (charA1 == charA2 ? "Yes" : "No" ) << std::endl; return 0 ; }
责任链模式
责任链模式的目的是避免请求发送者与多个接收者之间的耦合关系,将这些接收者组成一条链,并沿着这条链传递请求,直到有一个接收者处理它为止。
状态模式
状态模式 是一种行为型设计模式 ,它允许对象在内部状态改变时改变其行为。状态模式的关键思想是将对象的行为封装在不同的状态对象中,客户端无需关心状态的切换逻辑,只需调用对象的方法,具体行为由状态对象决定。
状态模式的优点是让状态和行为的变化独立于主对象,从而使得代码更易于维护和扩展。
示例:简单的电灯状态切换
假设我们有一个电灯,它可以处于**开(On)和 关(Off)**两种状态。我们希望通过状态模式控制电灯的行为(开或关),并能在不同状态下切换。
状态模式的类图:
State :LightState
ConcreteState :LightOnState
、LightOffState
Context :Light
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <iostream> #include <memory> class LightState {public : virtual void PressSwitch () = 0 ; virtual ~LightState () = default ; }; class LightOnState : public LightState {public : void PressSwitch () override { std::cout << "The light is already ON. Turning it OFF now.\n" ; } }; class LightOffState : public LightState {public : void PressSwitch () override { std::cout << "The light is OFF. Turning it ON now.\n" ; } }; class Light {private : std::unique_ptr<LightState> state; public : Light () : state (std::make_unique <LightOffState>()) {} void SetState (std::unique_ptr<LightState> newState) { state = std::move (newState); } void PressSwitch () { state->PressSwitch (); if (dynamic_cast <LightOffState*>(state.get ())) { SetState (std::make_unique <LightOnState>()); } else { SetState (std::make_unique <LightOffState>()); } } }; int main () { Light light; light.PressSwitch (); light.PressSwitch (); light.PressSwitch (); light.PressSwitch (); return 0 ; }
输出结果:
1 2 3 4 The light is OFF. Turning it ON now. The light is already ON. Turning it OFF now. The light is OFF. Turning it ON now. The light is already ON. Turning it OFF now.
代码说明:
状态接口(LightState
):定义了一个纯虚函数 PressSwitch()
,表示按下开关时的动作。
具体状态类(LightOnState
和 LightOffState
):
LightOnState
:表示灯处于打开状态时的行为。
LightOffState
:表示灯处于关闭状态时的行为。
上下文类(Light
):
它持有一个当前的状态对象(state
),并且通过 PressSwitch()
方法来响应用户操作。
每次按下开关时,电灯会根据当前状态切换到另一个状态。
学习自: