设计模式的原则: (https://www.cnblogs.com/dolphin0520/p/3919839.html)
1、单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
2、开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
3、里氏代换原则(Liskov Substitution Principle, LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。
4、依赖倒转原则(Dependency Inversion Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
5、接口隔离原则(Interface Segregation Principle, ISP):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
6、迪米特法则(Law of Demeter, LoD):一个软件实体应当尽可能少地与其他实体发生相互作用。迪米特法则可降低系统的耦合度,使类与类之间保持松散的耦合关系。
(https://www.cnblogs.com/chengjundu/p/8473564.html)
一、观察者模式
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
二、单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已存在单例,如果有则返回,没有则创建。
关键代码:构造函数是私有的。
单例大约有两种实现方法:懒汉与饿汉。
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化,所以上边的经典方法被归为懒汉实现;
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
特点与选择:
由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
在访问量较小时,采用懒汉实现。这是以时间换空间。
//懒汉式一般实现:非线程安全,getInstance返回的实例指针需要delete
//解决方法:程序启动后,在主线程中把单例都调用一遍。 class Singleton { public: static Singleton* getInstance(); ~Singleton(){} static void Release(); private: static Singleton* m_pSingleton; Singleton(){} Singleton(const Singleton& obj) = delete; //明确拒绝 Singleton& operator=(const Singleton& obj) = delete; //明确拒绝 }; Singleton* Singleton::m_pSingleton = NULL; Singleton* Singleton::getInstance() { if(m_pSingleton == NULL) { m_pSingleton = new Singleton; } return m_pSingleton; }
void Singleton::Release()
{
if(m_pSingleton)
{
delete m_pSingleton;
m_pSingleton = NULL;
}
} //END
//懒汉式:加lock,线程安全 std::mutex mt; class Singleton { public: static Singleton* getInstance(); private: Singleton(){} Singleton(const Singleton&) = delete; //明确拒绝 Singleton& operator=(const Singleton&) = delete; //明确拒绝 static Singleton* m_pSingleton; }; Singleton* Singleton::m_pSingleton = NULL; Singleton* Singleton::getInstance() { if(m_pSingleton == NULL) { mt.lock();
if(m_pSingleton == NULL) m_pSingleton = new Singleton(); mt.unlock(); } return m_pSingleton; } //END
//返回一个reference指向local static对象 //多线程可能存在不确定性:任何一种non-const static对象,不论它是local或non-local,在多线程环境下“等待某事发生”都会有麻烦。
//解决的方法:在程序的单线程启动阶段,手工调用所有reference-returning函数。 class Singleton { public: static Singleton& getInstance(); private: Singleton(){} Singleton(const Singleton&) = delete; //明确拒绝 Singleton& operator=(const Singleton&) = delete; //明确拒绝 }; Singleton& Singleton::getInstance() { static Singleton singleton; return singleton; } //END //饿汉式:线程安全,注意delete class Singleton { public: static Singleton* getInstance(); private: Singleton(){} Singleton(const Singleton&) = delete; //明确拒绝 Singleton& operator=(const Singleton&) = delete; //明确拒绝 static Singleton* m_pSingleton; }; Singleton* Singleton::m_pSingleton = new Singleton(); Singleton* Singleton::getInstance() { return m_pSingleton; } //END
三、工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式
3.1、简单工厂模式:主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类。
typedef enum { T80 = 1, T99 }TankType; class Tank { public: virtual void message() = 0; }; class Tank80:public Tank { public: void message() { cout << "Tank80" << endl; } }; class Tank99:public Tank { public: void message() { cout << "Tank99" << endl; } }; class TankFactory { public: Tank* createTank(TankType type) { switch(type) { case 1: return new Tank80(); case 2: return new Tank99(); default: return NULL; } } };
3.2、工厂方法模式
是指定义一个创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到其子类。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
缺点:每增加一种产品,就需要增加一个对象工厂。相比简单工厂模式,工厂方法模式需要定义更多的类。
class Tank { public: virtual void message() = 0; }; class Tank80:public Tank { public: void message() { cout << "Tank80" << endl; } }; class Tank99:public Tank { public: void message() { cout << "Tank99" << endl; } }; class TankFactory { public: virtual Tank* createTank() = 0; }; class Tank80Factory:public TankFactory { public: Tank* createTank() { return new Tank80(); } }; class Tank99Factory:public TankFactory { public: Tank* createTank() { return new Tank99(); } };
3.3、抽象工厂模式
提供一个创建一系列相关或相互依赖的对象接口,而无需指定它们的具体类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。
关键代码:在一个工厂里聚合多个同类产品。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
class Tank { public: virtual void message() = 0; }; class Tank80:public Tank { public: void message() { cout << "Tank80" << endl; } }; class Tank99:public Tank { public: void message() { cout << "Tank99" << endl; } }; class Plain { public: virtual void message() = 0; }; class Plain80: public Plain { public: void message() { cout << "Plain80" << endl; } }; class Plain99: public Plain { public: void message() { cout << "Plain99" << endl; } }; class Factory { public: virtual Tank* createTank() = 0; virtual Plain* createPlain() = 0; }; class Factory80:public Factory { public: Tank* createTank() { return new Tank80(); } Plain* createPlain() { return new Plain80(); } }; class Factory99:public Factory { public: Tank* createTank() { return new Tank99(); } Plain* createPlain() { return new Plain99(); } };
四、策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。使得算法可以独立于使用它的客户而变化,也就是说这些算法所完成的功能是一样的,对外接口是一样的,只是各自现实上存在差异。
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
//传统策略模式实现 class Hurt { public: virtual void redBuff() = 0; }; class AdcHurt:public Hurt { public: void redBuff() { cout << "Adc hurt" << endl; } }; class ApcHurt:public Hurt { public: void redBuff() { cout << "Apc hurt" << endl; } }; //方法1:传入一个指针参数 class Soldier { public: Soldier(Hurt* hurt):m_hurt(hurt) { } ~Soldier() { } void beInjured() { m_hurt->redBuff(); } private: Hurt* m_hurt; }; //方法2:传入一个参数标签 typedef enum { adc, apc }HurtType; class Master { public: Master(HurtType type) { switch(type) { case adc: m_hurt = new AdcHurt; break; case apc: m_hurt = new ApcHurt; break; default: m_hurt = NULL; break; } } ~Master() { } void beInjured() { if(m_hurt != NULL) { m_hurt->redBuff(); } else { cout << "Not hurt" << endl; } } private: Hurt* m_hurt; }; //方法3:使用模板类 template <typename T> class Tank { public: void beInjured() { m_hurt.redBuff(); } private: T m_hurt; }; //END //使用函数指针实现策略模式 void adcHurt(int num) { cout << "adc hurt:" << num << endl; } void apcHurt(int num) { cout << "apc hurt:" << num << endl; } //普通函数指针 class Aid { public: typedef void (*HurtFun)(int); Aid(HurtFun fun):m_fun(fun) { } void beInjured(int num) { m_fun(num); } private: HurtFun m_fun; }; //使用std::function , 头文件:#include<functional> class Bowman { public: typedef std::function<void(int)> HurtFunc; Bowman(HurtFunc fun):m_fun(fun) { } void beInjured(int num) { m_fun(num); } private: HurtFunc m_fun; }; //END
五、模板模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:多个子类有相同的方法,并且逻辑相同,细节有差异。
如何解决:对重要,复杂的算法,将核心算法设计为模板方法,周边细节由子类实现,重构时,经常使用的方法,将相同的代码抽象到父类,通过钩子函数约束行为。
关键代码:在抽象类实现通用接口,细节变化在子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
class Computer { public: void product() //通用接口 { installCpu(); installRam(); installGraphicsCard(); } protected: virtual void installCpu() = 0; virtual void installRam() = 0; virtual void installGraphicsCard() = 0; }; class ComputerA:public Computer { protected: void installCpu() override { cout << "ComputerA install Inter Core i5" << endl; } void installRam() override { cout << "ComputerA install 2G Ram" << endl; } void installGraphicsCard() override { cout << "ComputerA install Gtx940 GraphicsCard" << endl; } }; class ComputerB:public Computer { protected: void installCpu() override { cout << "ComputerB install Inter Core i7" << endl; } void installRam() override { cout << "ComputerB install 4G Ram" << endl; } void installGraphicsCard() override { cout << "ComputerB install Gtx960 GraphicsCard" << endl; } };
六、原型模式
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
主要解决:在运行期建立和删除对象。
何时使用:
1).当我们的对象类型不是开始就能确定的,而这个类型是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的对象比较容易一些;
2).有的时候,我们需要一个对象在某个状态下的副本,此时,我们使用原型模式是最好的选择;例如:一个对象,经过一段处理之后,其内部的状态发生了变化;这个时候,我们需要一个这个状态的副本,如果直接new一个新的对象的话,但是它的状态是不对的,此时,可以使用原型模式,将原来的对象拷贝一个出来,这个对象就和之前的对象是完全一致的了;
3).当我们处理一些比较简单的对象时,并且对象之间的区别很小,可能就几个属性不同而已,那么就可以使用原型模式来完成,省去了创建对象时的麻烦了;
4).有的时候,创建对象时,构造函数的参数很多,而自己又不完全的知道每个参数的意义,就可以使用原型模式来创建一个新的对象,不必去理会创建的过程。
->适当的时候考虑一下原型模式,能减少对应的工作量,减少程序的复杂度,提高效率
如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。
关键代码:拷贝,return new className(*this);
class A { public: A() { } virtual ~A() { } virtual A* clone() = 0; virtual void show() = 0; }; class Sheep:public A { public: Sheep(int id, string name):m_id(id),m_name(name) { cout << "Sheep() id add:" << id << endl; cout << "Sheep() name add:" << name.c_str() << endl; } ~Sheep() { } Sheep(const Sheep& obj) { this->m_id = obj.m_id; this->m_name = obj.m_name; cout << "Sheep(const Sheep& obj) id add:" << m_id << endl; cout << "Sheep(const Sheep& obj) name add:" << m_name.c_str() << endl; } A* clone() { return new Sheep(*this); } void show() { cout << "id :" << m_id << endl; cout << "name:" << m_name.data() << endl; } private: int m_id; string m_name; }; int main() { A* s1 = new Sheep(1, "abs"); s1->show(); A* s2 = s1->clone(); s2->show(); delete s1; delete s2; return 0; }
七、建造者模式:将复杂对象的构建和其表示分离,使得同样的构建过程可以创建不同的表示。
主要解决:一个复杂对象的创建工作,由各个部分的子对象用一定的算法构成;由于需求变化,这个复杂对象的各个部分经常面临变化,但将它们组合在一起的算法却相对稳定。
如何解决:将变与不变分开
关键代码:建造者:创建和提供实例,Director:管理建造出来的实例的依赖关系。。
缺点:1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
typedef enum { type1, type2 }ProductType; class Product //产品 { public: void setNum(int num); void setColor(string color); void setType(ProductType type); void showProduct(); private: int m_num; string m_color; ProductType m_type; }; void Product::setNum(int num) { m_num = num; } void Product::setColor(string color) { m_color = color; } void Product::setType(ProductType type) { m_type = type; } void Product::showProduct() { cout << "Product: " << endl; cout << " num : " << m_num << endl; cout << " color: " << m_color.data() << endl; cout << " type : " << m_type << endl; } //建造者父类,定义接口 class Builder { public: Builder(){} virtual ~Builder(){} virtual void buildNum(int num) = 0; virtual void buildColor(string color) = 0; virtual void buildType(ProductType type) = 0; virtual void createProduct() = 0; virtual Product* getProduct() = 0; virtual void show() = 0; }; //建造者A class BuilderA:public Builder { public: BuilderA(){} ~BuilderA(){} void buildNum(int num) override; void buildColor(string color) override; void buildType(ProductType type) override; void createProduct() override; Product* getProduct() override; void show() override; private: Product* m_product; }; void BuilderA::buildNum(int num) { cout << "BuilderA build Num: " << num << endl; m_product->setNum(num); } void BuilderA::buildColor(string color) { cout << "BuilderA build color: " << color.data() << endl; m_product->setColor(color); } void BuilderA::buildType(ProductType type) { cout << "BuilderA build type: " << type << endl; m_product->setType(type); } void BuilderA::createProduct() { cout << "BuilderA CreateProduct: " << endl; m_product = new Product(); } Product* BuilderA::getProduct() { return m_product; } void BuilderA::show() { m_product->showProduct(); } //建造者B class BuilderB:public Builder { public: BuilderB(){} ~BuilderB(){} void buildNum(int num) override; void buildColor(string color) override; void buildType(ProductType type) override; void createProduct() override; Product* getProduct() override; void show() override; private: Product* m_product; }; void BuilderB::buildNum(int num) { cout << "BuilderB build Num: " << num << endl; m_product->setNum(num); } void BuilderB::buildColor(string color) { cout << "BuilderB build color: " << color.data() << endl; m_product->setColor(color); } void BuilderB::buildType(ProductType type) { cout << "BuilderB build type: " << type << endl; m_product->setType(type); } void BuilderB::createProduct() { cout << "BuilderB CreateProduct: " << endl; m_product = new Product(); } Product* BuilderB::getProduct() { return m_product; } void BuilderB::show() { m_product->showProduct(); } //管理类,负责安排构造的具体过程 class Director { public: Director(Builder* builder):m_builder(builder) { } void construct(int num, string color, ProductType type) { if(m_builder) { m_builder->createProduct(); m_builder->buildNum(num); m_builder->buildColor(color); m_builder->buildType(type);
m_builder->show(); } } private: Builder* m_builder; }
八、职责链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之前的耦合关系,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无需关心请求的处理细节和请求的传递,所有职责链将请求的发送者和请求的处理者解耦了。
如何解决:职责链链扣类都现实统一的接口。
关键代码:Handler内指明其上级,handleRequest()里判断是否合适,不合适则传递给上级。
enum RequestLevel { One = 1, Two, Three }; class Leader { public: Leader(Leader* leader):m_leader(leader){} virtual ~Leader(){} virtual void handleRequest(RequestLevel level) = 0; protected: Leader* m_leader; }; class Monitor:public Leader //链扣1 { public: Monitor(Leader* leader):Leader(leader){} void handleRequest(RequestLevel level) { if(level < Two) { cout << "Mointor handle request : " << level << endl; } else if(m_leader) { m_leader->handleRequest(level); } } }; class Captain:public Leader //链扣2 { public: Captain(Leader* leader):Leader(leader){} void handleRequest(RequestLevel level) { if(level < Three) { cout << "Captain handle request : " << level << endl; } else if(m_leader) { m_leader->handleRequest(level); } } }; class General:public Leader //链扣3 { public: General(Leader* leader):Leader(leader){} void handleRequest(RequestLevel level) { cout << "General handle request : " << level << endl; } }; int main() { Leader* general = new General(nullptr); Leader* captain = new Captain(general); Leader* monitor = new Monitor(captain); monitor->handleRequest(Three); return 0; }
九、