网站推荐:
https://refactoringguru.cn/design-patterns/creational-patterns
简单工厂:
简单工厂就是根据入参的类型进行判断需要创建那种对象,只不过返回值是一个基类指针,以此来完成返回值类型的统一。
抽象工厂(Abstract Factory):
一系列接口,用于创建一系列相关或相互依赖的类。
工厂方法(Factory Method):
定义一个创建对象的接口,让子类决定将哪个类实例化。比如,定义一个工厂基类,这个基类包含一个创建对象的接口,所有工厂都要继承这个基类并实现接口。抽象工厂的核心理念是定义工厂基类,然后派生不同的工厂。
这三种工厂模式的区别:https://www.zhihu.com/question/20367734
适配器(Adapter):
对已有类的对外成员函数进行二次包装,方便其他类使用,目的在于 已有类 和 其他类都不需要变动,这其实就相当于做一个接口上的互相兼容。使用场景主要就是有些陈旧而复杂的两个模块,如果不希望通过修改代码来完成模块的兼容,那么就可以增加一个Adapter,这样接口提供者不需要修改接口,接口使用者也不需要修改自己的业务代码。
或者
桥(Bridge):
将抽象部分和实现部分分离,是他们都能独立地变化,比如如果多个类都有相似或同样的成员函数,那么可以把这些函数都归纳为一个接口类,然后让那些类都继承这个接口类。
初始化代理(Builder):
如果某个对象的创建特别复杂,那么可以把创建过程集成到一个Builder类中。
责任链(Chain of Responsibility):
将事件封装成一个类,然后把事件传递给一系列有继承层级关系的类实例链表中,这些类都有着统一的虚函数接口,这个接口接收事件类作为参数,由链表中的每个节点自行决定是否处理这个事件。可以对比Qt中的 Event系统,每个对象都有自己的 Event Handler函数,并在这些函数中决定是否处理Event,甚至可以决定是否截获或者继续向下传递。
class Event {
//...
public:
char i;
//...
};
class A{
//...
public:
virtual bool isMyEvent(Event* e) {
if (e->i = 'A') return true;
else return false;
}
virtual void eventHandler(Event* e) {
if (isMyEvent(e)) {
doWorkofA(e);
}
else {
//谁都不要的Event
}
}
void doWorkofA(Event* e) {
//做点啥
}
//...
};
class B :public A {
//...
public:
virtual bool isMyEvent(Event* e) {
if (e->i = 'B') return true;
else return false;
}
virtual void eventHandler(Event* e) {
if (isMyEvent(e)) {
doWorkofB(e);
}
else {
A::eventHandler(e); //传给父类处理
}
}
void doWorkofB(Event* e) {
//做点啥
}
//...
};
class C :public B {
//...
virtual bool isMyEvent(Event* e) {
if (e->i = 'C') return true;
else return false;
}
virtual void eventHandler(Event* e) {
if (isMyEvent(e)) {
doWorkofC(e);
}
else {
A::eventHandler(e); //这里可以选择随意传给谁处理
}
}
void doWorkofC(Event* e) {
//做点啥
}
//...
};
注:责任链模式有几个要点:1)最顶层的基类要进行事件兜底;2)如果事件不归自己处理那么要通过 父类名::虚函数 的方式把事件交给父类的虚函数处理;3)因为是代码控制流程,所以链条可以分叉/截断/跳级传送等等,这就要在代码的判断虚函数中人为控制。
命令(Command):
将请求封装成类,这些类都继承自同一个基类,每个类都实现基类的虚函数,这个虚函数内是属于当前Command的业务代码,然后让其他类可以接收和使用这个对象。还是对比Qt的Evenet系统,所有的事件都继承自QEvent ,这个设计模式在日常代码中经常使用,只是很多时候我们都没发现而已。为了方便记忆,我们可以类比Linux的命令行,每个命令都继承自一个命令类,然后各自实现自己的业务,这些命令都会被 bash 接受和执行内部的虚函数,bash只需要调用对应的虚函数即可。
组合(Composite):
将对象组合成树形结构,这种设计模式主要用来让客户对单个对象和符合对象使用具有一致性。
注:组合模式的核心就是嵌套,实现抽象类的具体类内部又包含N个抽象类,具体类内部提供遍历自身的操作,这样就可以保证了一个树形结构,每个节点都可以调用遍历接口来绘制自己的所有子节点。这种模式不是很特别,因此我们即便用到了可能也不知情。
修饰者(Decorator):
动态地给一个对象添加一些额外的职责或功能。这种方式比生成子类更加灵活。注意这里是给对象添加额外内容,这有别于继承某个接口而实现某个功能,继承是针对类的,这里是针对对象的。我们可以在Qt中找到相应的应用和实现,那就是Qt的动态属性功能,Qt可以给已经创建的实例添加动态属性以方便功能扩展。
注:图中Decorator的component成员就是那个需要被修饰的对象
面(Facade):
为子系统的一组接口提供一个一直的界面,主要是为了让子系统的接口更加好用。
共享元素(Flyweight):
运用共享技术有效地支持大量细粒度的对象。
解释器(Interpreter):
定义一个语言,定义它的文法表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
迭代器(Iterator):
提供对聚合对象的统一访问接口,不管聚合对象的内部数据结构是什么样的。
中介(Mediator):
用一个中介对象封装一系列对象交互,这些被代理的对象不需要互相直接交互,全都通过中介来进行。
备忘录(Memento):
在不破坏封装性的前提下,捕获一个对象的状态并保存这个状态,这个状态是一个实例,这样方便之后切换状态。
观察者(Observer):
向某个被观察对象注册一些监听者实例,当某个事件发生时,被观察对象主动通知所有监听者实例发生了什么事情。实现起来很简单,所有监听者都继承自同一个父类,同时实现父类的事件触发函数,被观察对象持有一个列表,里面存储N个父类指针,当事件发生时,被观察对象遍历并调用列表中所有实例的事件处理虚函数。
原型(Prototype):
原型时一系列实例,如果想创建新的实例,那么可以通过拷贝原型的方式来创建。
代理(Proxy):
对已有对象提供一个接口再封装,如果已有对象的封装性较差,那么大部分情况下要采用Proxy进行再次封装。
单例(Sigleton):
保证全局范围内只有一个实例。
状态机(State):
对象根据业务功能的转变而具备不同的状态,以及可提供不同的服务。状态机的实现多种多样,不好一概而论。
策略(Strategy):
定义一系列算法,这些算法继承自同一个基类,所有需要这些算法的实例都持有一个基类算法的引用,后期只需要根据需要切换引用所指向的算法策略即可。这就是简单的多态应用。
模板方法(Template Method):
定义一个类,内含一套流程,流程中包含自定义的部分和多态的部分,这样新的有流程便可以通过继承的方式来获得包含自定义多态的新流程模板。
访问者(Visitor):
表示一个作用于某对象结构的各元素的操作,使得可以在不改变各元素的类的前提下定义作用于这些元素的新操作。