目录
0. 设计模式概述
0.1、定义
设计模式就是经由无数前人的实践,在设计过程中能够反复使用、解决特定问题的设计方法。(针对设计问题的通用解决方案)
0.2、分类
1、创建型模式,5种
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
2、结构型模式,7种
适配器模式、装饰器模式、外观模式、桥接模式、组合模式、享元模式、代理模式
3、行为型模式,11种
策略模式、模版方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
0.3、为什么适用设计模式
可以把它应用到特定的应用中,用于解决相似的问题。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
0.4、面向对象程序设计(OOP)的设计模式的五大原则
1、单一职责原则
英文名称是Single Responsibility Principle,简称SRP。定义为就一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数、数据的封装。
单一职责有2个含义,一个是避免相同的职责分散到不同的类中,另一个是避免一个类承担太多职责。减少类的耦合,提高类的复用性。
2、接口隔离原则
英文全称是InterfaceSegregation Principles,简称ISP。使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
每个接口所要的实现的功能应该要最小化,只要定义出自己仅需的接口函数就行。
简单说,就是使用多个专门的接口比使用单个接口好很多。
3、开放-封闭原则
英文全称是Open Close Principle,简称OCP。一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。 即软件实体应尽量在不修改原有代码的情况下进行扩展。
软件的实现完美情况。大概就是说在实现新功能的实话,不需要修改你的代码,只需要拓展代码来实现新的功能。
核心思想就是对抽象编程,而不对具体编程。
4、替换原则
所有引用基类的地方必须能透明地使用其子类的对象。
只要 父类 能出现的地方 子类 就可以出现,而且替换为 子类 也不产生任何异常错误,反之则不然。这主要体现在,我们经常使用抽象类/基类做为方法参数,具体使用哪个子类作为参数传入进去,由调用者决定。
主要针对继承的设计原则
1)子类必须完全实现父类的方法,子类可以有自己的个性外观(属性)和行为(方法)
2)在客户端程序中只应该使用父类对象而不应当直接使用子类对象,这样可以实现运行期间绑定。
5、依赖倒置原则
抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。 关键点: 1. 高层模块不应该依赖低层模块,两者都应该依赖其抽象 2. 抽象不应该依赖细节 3. 细节应该依赖抽象
上层模块不应该依赖于下层模块,他们共同依赖于一个抽象,即:父类不能依赖子类,他们都要依赖抽象类。
抽象不能依赖于具体,具体应该要依赖于抽象。
0.5、Rereference
1. 单例模式
作用:单例模式主要解决一个全局使用的类频繁的创建和销毁的问题。保证一个类只有一个实例,并提供一个访问它的全局访问点,使得系统中只有唯一的一个对象实例。实现单例模式必须注意一下几点:
-
单例类只能由一个实例化对象。
-
单例类必须自己提供一个实例化对象。
-
单例类必须提供一个可以访问唯一实例化对象的接口。
应用:常用于管理资源,如日志、线程池
实现要点
-
在类中,要构造一个实例,就必须调用类的构造函数,并且为了保证全局只有一个实例, 需防止在外部调用类的构造函数而构造实例,需要将构造函数的访问权限标记为private
-
同时阻止拷贝创建对象时赋值时拷贝对象,因此也将它们声明并权限标记为private;
-
需要提供一个全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例。
class Singleton{
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}
void printTest(){
cout<<"do something"<<endl;
}
private:
Singleton(){}//防止外部调用构造创建对象
Singleton(Singleton const &singleton);//阻止拷贝创建对象
Singleton& operator=(Singleton const &singleton);//阻止赋值对象
};
int main()
{
Singleton &a=Singleton::getInstance();
a.printTest();
return 0;
}
(1)首先,构造函数声明成private的目的是只允许内部调用,getInstance()中的静态局部变量创建时调用,但不允许外部调用构造创建第二个实例;
(2)然后,拷贝构造和拷贝赋值符是声明成了private而不给出定义,其目的是阻止拷贝,如果企图通过拷贝构造来创建第二个实例,编译器会报错。
阻止拷贝的另一种写法是声明后接一个"=delete",也能起到相同的作用(C++11)。
单例模式分为懒汉和饿汉两种实现方式。
懒汉模式:在getinstance中实例化
饿汉模式:在单例类定义时实例化
1.1、懒汉单例模式
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化一个对象。在访问量较小,甚至可能不会去访问的情况下,采用懒汉实现,这是以时间换空间。
1.1.1、非线程安全的懒汉单例模式
/*
* 关键代码:构造函数是私有的,不能通过赋值运算,拷贝构造等方式实例化对象。
*/
//懒汉式一般实现:非线程安全,getInstance返回的实例指针需要delete
class Singleton
{
public:
static Singleton* getInstance();
~Singleton(){}
private:
Singleton(){} //构造函数私有
Singleton(const Singleton& obj) = delete; //明确拒绝
Singleton& operator=(const Singleton& obj) = delete; //明确拒绝
static Singleton* m_pSingleton;
};
Singleton* Singleton::m_pSingleton = NULL;
Singleton* Singleton::getInstance()
{
if(m_pSingleton == NULL)
m_pSingleton = new Singleton;
return m_pSingleton;
}
1.1.2、线程安全的懒汉单例模式–双重锁机制
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;
}
1.2、饿汉模式
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
饿汉模式不需要用锁,就可以实现线程安全。原因在于,在程序运行时就定义了对象,并对其初始化。之后,不管哪个线程调用成员函数getinstance(),都只不过是返回一个对象的指针而已。线程安全的,不需要在获取实例的成员函数中加锁.
//饿汉式:线程安全,注意一定要在合适的地方去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;
}
2. 工厂模式
作用:工厂模式主要解决接口选择的问题。该模式下定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,使其创建过程延迟到子类进行。
(工厂模式的主要作用是封装对象的创建,分离对象的创建和操作过程,用于批量管理对象的创建过程,便于程序的维护和扩展。工厂模式包括三种:简单工厂模式、工厂方法模式、抽象工厂模式。)
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。工厂模式作为一种创建模式,一般在创建复杂对象时,考虑使用;在创建简单对象时,建议直接new完成一个实例对象的创建。
2.1、简单工厂模式
主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类。使用简单工厂模式,我们只需要知道具体的产品型号就可以创建一个产品。
缺点:工厂类集中了所有产品类的创建逻辑,如果产品量较大,会使得工厂类变的非常臃肿。
//简单工厂模式
typedef enum ProductTypeTag
{
TypeA,
TypeB,
TypeC
}PRODUCTTYPE;
class Product//产品抽象基类
{
public:
virtual void Show() = 0;
};
class ProductA : public Product //子类A
{
public:
void Show()
{
cout<<"I'm ProductA"<<endl;
}
};
class ProductB : public Product //子类B
{
public:
void Show()
{
cout<<"I'm ProductB"<<endl;
}
};
class ProductC : public Product //子类C
{
public:
void Show()
{
cout<<"I'm ProductC"<<endl;
}
};
class Factory//工厂类
{
public:
Product* CreateProduct(PRODUCTTYPE type)
{
switch (type)
{
case TypeA:
return new ProductA();
case TypeB:
return new ProductB();
case TypeC:
return new ProductC();
default:
return NULL;
}
}
};
int main()
{
Factory productCreator;
Product *productA=productCreator.CreateProduct(TypeA);
Product *productB=productCreator.CreateProduct(TypeB);
Product *productC=productCreator.CreateProduct(TypeC);
productA->Show();
productB->Show();
productC->Show();
if(productA){
delete productA;
productA=NULL;
}
if(productB){
delete productB;
productB=NULL;
}
if(productC){
delete productC;
productC=NULL;
}
return 0;
}
2.2、工厂方法模式
定义一个创建对象的接口,其子类去具体现实这个接口以完成具体的创建工作。如果需要增加新的产品类,只需要扩展一个相应的工厂类即可。
缺点:产品类数据较多时,需要实现大量的工厂类,这无疑增加了代码量。
//工厂方法模式
typedef enum ProductTypeTag
{
TypeA,
TypeB,
TypeC
}PRODUCTTYPE;
class Product//产品抽象基类
{
public:
virtual void Show() = 0;
};
class ProductA : public Product //具体的产品类A
{
public:
void Show()
{
cout<<"I'm ProductA"<<endl;
}
};
class ProductB : public Product //具体的产品类B
{
public:
void Show()
{
cout<<"I'm ProductB"<<endl;
}
};
//抽象工厂类,提供一个创建接口
class Factory
{
public:
//提供创建产品实例的接口,返回抽象产品类
virtual Product *createProduct()=0;
};
//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class FactoryA:public Factory
{
public:
Product *createProduct()
{
return new ProductA();
}
};
//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class FactoryB:public Factory
{
public:
Product *createProduct()
{
return new ProductB();
}
};
//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class FactoryC:public Factory
{
public:
Product *createProduct(){
return new ProductC();
}
};
int main()
{
Factory *factoryA=new FactoryA(); //将子类对象传给基类指针实现运行时多态
Product *productA = factoryA->createProduct();
productA->Show();
Factory *factoryB=new FactoryB();
Product *productB = factoryB->createProduct();
productB->Show();
if (factoryA) {
delete factoryA;
factoryA = NULL;
}
if (factoryB) {
delete factoryB;
factoryB = NULL;
}
if (productA) {
delete productA;
productA = NULL;
}
if (productB) {
delete productB;
productB = NULL;
}
return 0;
}
2.3、抽象工厂模式
-
抽象工厂模式提供创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。当存在多个产品系列,而客户端只使用一个系列的产品时,可以考虑使用抽象工厂模式。
-
抽象工厂模式对工厂方法模式进行了更加一般化的描述。工厂方法模式适用于产品种类结构单一的场合,为一类产品提供创建的接口;而抽象工厂方法适用于产品种类结构多的场合,就是当具有多个抽象产品类型时,抽象工厂便可以派上用场。
-
抽象工厂模式更适合实际情况,受生产线所限,让低端工厂生产不同种类的低端产品,高端工厂生产不同种类的高端产品。
缺点:当增加一个新系列的产品时,不仅需要现实具体的产品类,还需要增加一个新的创建接口,扩展相对困难
/*
* 关键代码:在一个工厂里聚合多个同类产品。
*/
//抽象工厂模式
class ProductA
{
public:
virtual void Show() = 0;
};
class ProductA1 : public ProductA//A类低端产品
{
public:
void Show()
{
cout<<"I'm ProductA1"<<endl;
}
};
class ProductA2 : public ProductA//A类高端产品
{
public:
void Show()
{
cout<<"I'm ProductA2"<<endl;
}
};
class ProductB
{
public:
virtual void Show() = 0;
};
class ProductB1 : public ProductB//B类低端产品
{
public:
void Show()
{
cout<<"I'm ProductB1"<<endl;
}
};
class ProductB2 : public ProductB//B类高端产品
{
public:
void Show()
{
cout<<"I'm ProductB2"<<endl;
}
};
class Factory //工厂抽象类
{
public:
virtual ProductA *CreateProductA() = 0;
virtual ProductB *CreateProductB() = 0;
};
class Factory1 : public Factory//1号工厂用于生产低端产品
{
public:
ProductA *CreateProductA()
{
return new ProductA1();
}
ProductB *CreateProductB()
{
return new ProductB1();
}
};
class Factory2 : public Factory//2号工厂用于生产高端产品
{
ProductA *CreateProductA()
{
return new ProductA2();
}
ProductB *CreateProductB()
{
return new ProductB2();
}
};
int main()
{
Factory *factory1 = new Factory1();
ProductA *productA1 = factory1->CreateProductA();
ProductB *productB1 = factory1->CreateProductB();
productA1->Show();
productB1->Show();
Factory *factory2 = new Factory2();
ProductA *productA2 = factory2->CreateProductA();
ProductB *productB2 = factory2->CreateProductB();
productA2->Show();
productB2->Show();
if (factory1)
{
delete factory1;
factory1 = NULL;
}
if (productA1)
{
delete productA1;
productA1= NULL;
}
if (productB1)
{
delete productB1;
productB1 = NULL;
}
if (factory2)
{
delete factory2;
factory2 = NULL;
}
if (productA2)
{
delete productA2;
productA2 = NULL;
}
if (productB2)
{
delete productB2;
productB2 = NULL;
}
}
3.观察者模式
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都要得到通知并自动更新。
观察者模式从根本上讲必须包含两个角色:观察者和被观察对象。
-
被观察对象自身应该包含一个容器来存放观察者对象,当被观察者自身发生改变时通知容器内所有的观察者对象自动更新。
-
观察者对象可以注册到被观察者的中,完成注册后可以检测被观察者的变化,接收被观察者的通知。当然观察者也可以被注销掉,停止对被观察者的监控。
/*
* 关键代码:在目标类中增加一个ArrayList来存放观察者们。
*/
#include <iostream>
#include <list>
#include <memory>
using namespace std;
class View;
//被观察者抽象类 数据模型
class DataModel
{
public:
virtual ~DataModel(){}
virtual void addView(View* view) = 0;
virtual void removeView(View* view) = 0;
virtual void notify() = 0; //通知函数
};
//观察者抽象类 视图
class View
{
public:
virtual ~View(){ cout << "~View()" << endl; }
virtual void update() = 0;
virtual void setViewName(const string& name) = 0;
virtual const string& name() = 0;
};
//具体的被观察类, 整数模型
class IntDataModel:public DataModel
{
public:
~IntDataModel()
{
m_pViewList.clear();
}
virtual void addView(View* view) override
{
shared_ptr<View> temp(view);
auto iter = find(m_pViewList.begin(), m_pViewList.end(), temp);
if(iter == m_pViewList.end())
m_pViewList.push_front(temp);
else
cout << "View already exists" << endl;
}
void removeView(View* view) override
{
auto iter = m_pViewList.begin();
for(; iter != m_pViewList.end(); iter++) {
if((*iter).get() == view) {
m_pViewList.erase(iter);
cout << "remove view" << endl;
return;
}
}
}
virtual void notify() override
{
auto iter = m_pViewList.begin();
for(; iter != m_pViewList.end(); iter++)
(*iter).get()->update();
}
private:
list<shared_ptr<View>> m_pViewList;
};
//具体的观察者类 表视图
class TableView : public View
{
public:
TableView() : m_name("unknow"){}
TableView(const string& name) : m_name(name){}
~TableView(){ cout << "~TableView(): " << m_name.data() << endl; }
void setViewName(const string& name)
{
m_name = name;
}
const string& name()
{
return m_name;
}
void update() override
{
cout << m_name.data() << " update" << endl;
}
private:
string m_name;
};
int main()
{
/*
* 这里需要补充说明的是在此示例代码中,View一旦被注册到DataModel类之后,DataModel解析时会自动解析掉 * 内部容器中存储的View对象,因此注册后的View对象不需要在手动去delete,再去delete View对象会出错。
*/
View* v1 = new TableView("TableView1");
View* v2 = new TableView("TableView2");
View* v3 = new TableView("TableView3");
View* v4 = new TableView("TableView4");
IntDataModel* model = new IntDataModel;
model->addView(v1);
model->addView(v2);
model->addView(v3);
model->addView(v4);
model->notify();
cout << "-------------\n" << endl;
model->removeView(v1);
model->notify();
delete model;
model = nullptr;
return 0;
}
4. 装饰器模式
装饰模式:动态地给一个对象添加一些额外的功能,它是通过创建一个包装对象,也就是装饰来包裹真实的对象。新增加功能来说,装饰器模式比生产子类更加灵活。
对已经存在的某些类进行装饰,以此来扩展一些功能,从而动态的为一个对象增加新的功能。装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
以下情形考虑使用装饰模式:
-
需要扩展一个类的功能,或给一个类添加附加职责。
-
需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
-
需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
-
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
/*
* 关键代码:1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
*/
#include <iostream>
using namespace std;
//抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
class Component
{
public:
virtual ~Component(){}
virtual void configuration() = 0;
};
//具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
class Car : public Component
{
public:
void configuration() override
{
cout << "A Car" << endl;
}
};
//装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
class DecorateCar : public Component
{
public:
DecorateCar(Component* car) : m_pCar(car){}
void configuration() override
{
m_pCar->configuration();
}
private:
Component* m_pCar;
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateLED : public DecorateCar
{
public:
DecorateLED(Component* car) : DecorateCar(car){}
void configuration() override
{
DecorateCar::configuration();
addLED();
}
private:
void addLED()
{
cout << "Install LED" << endl;
}
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecoratePC : public DecorateCar
{
public:
DecoratePC(Component* car) : DecorateCar(car){}
void configuration() override
{
DecorateCar::configuration();
addPC();
}
private:
void addPC()
{
cout << "Install PC" << endl;
}
};
//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateEPB : public DecorateCar
{
public:
DecorateEPB(Component* car) : DecorateCar(car){}
void configuration() override
{
DecorateCar::configuration();
addEPB();
}
private:
void addEPB()
{
cout << "Install Electrical Park Brake" << endl;
}
};
int main()
{
Car* car = new Car;
DecorateLED* ledCar = new DecorateLED(car);
DecoratePC* pcCar = new DecoratePC(ledCar);
DecorateEPB* epbCar = new DecorateEPB(pcCar);
epbCar->configuration();
delete epbCar;
epbCar = nullptr;
delete pcCar;
pcCar = nullptr;
delete ledCar;
ledCar = nullptr;
delete car;
car = nullptr;
return 0;
}