C++设计模式
最近在设计一个框架,所以打算把C++的设计模式在复习一遍,看看有哪些东西是可以用起来的,可以参考的网站是Refactoring Guru。
基础概念
类型体系
类型体系就是在根类型基础上,通过继承而得到的很多类型,这些所有的类型统称为类型体系;类型体系分层可以按照继承次数来计算,就像一棵树计算深度,但是从设计模式角度看,从接口来区分子类型体系是个更好的方式,在子类型体系中可以用这个层次上的基类指针来表示整个类型体系去解耦。
解耦
我对解耦的理解是,两个类型体系相互传递基类指针调用接口,因为基类指针指向了不同的类型,因此实现了接口的动态绑定,但是两个基类指针对应的类型体系各自就是解耦的。除此之外,类型体系之间的依赖关系,我们可以通过构造函数传入基类指针来实现。
经常说的组合优于继承,我理解本质上来说就是加类型体系比在类型体系上增加类型更好,因为增加类型体系就是增加了基类指针数量,这样就能够实现更多的解耦。
并不是所有的设计模式都依赖虚函数,有些设计模式是用于解耦类型体系的,有些设计模式我理解是一种惯用法。结构型设计模式更像是战略,组织了整体的代码;行为型设计模式像是战术,在具体的一些逻辑代码中可以进行优化。
UML
依赖->关联->聚合->组合,这四个关系是层层递进的。 依赖关系通常是两个类型体系之间互相传参数 关联关系通常是一个类型体系是另一个类型体系的成员,但是两者是对等的 聚合关系通常是在关联关系基础上,一个类型体系的功能需要另一个类型体系来实现 组合关系通常是在聚合关系基础上,一个类型体系的生命周期被另一个类型体系掌握
结构相关
我的理解是多个类型体系之间的结构关系,是否要成为对方的数据成员,还是通过构造函数传入或者通过接口函数传入。
外观模式
外观模式指的是对外提供了一系列接口,这些接口的内部实现又是靠多个子类型体系支撑的,这些子类型体系通过一系列的指针作为成员,提供各自的功能。这些子类型体系对应的指针应该在构造函数中去初始化。
桥接模式
桥接模式指的是某个接口的实现依赖外部的一个指针对应的类型体系支持,因此需要有个函数需要声明形参是这个类型体系的基类指针。
装饰模式
我理解就是如果某个类型的接口的功能需要和类型体系本身解耦,那么需要将这个功能本身抽象成一个类型出来,这个类型不是一个具体的事物的抽象,而是功能的抽象。这个功能抽象成为类型体系后,接口上需要将形参传入原有类型的基类指针,而且由于是依赖于原有的类型体系,所以构造函数中就要传入这个依附类型体系的基类指针。
三个模式之间的求同存异
我理解桥接模式和装饰模式都是从外观模式延伸出来的,外观模式是写代码最自然的理解,只要是写代码的都有这种思维。
外观模式和桥接模式其实有一些共同点,就是自己接口的功能,是需要通过其他类型体系来支撑的;但是两者的区别是这个类型体系的基类指针是否需要在构造的时候就初始化,外观模式需要,桥接模式是外部传入的。
装饰模式和桥接模式正好是一个被动和主动的关系;装饰模式的被动表示在,装饰类型体系依赖于被装饰类型体系,因此构造函数中就要传入被装饰类型体系;桥接模式是类型体系的某个功能依赖于其他类型体系的实现,因此调用接口的时候传入这个类型体系的基类指针即可。
组合模式
强调整个类型体系不仅仅有继承关系,还有属性结构。基类的实现中,要有一个存自己类型指针的list。比如一个公司组织架构的抽象,可以先抽象出一个CompanyNode类型,部门和子部门等概念都是通过这个子类型进行派生的。
class CompanyNode{
list<CompanyNode*> m_list_company_nodes;
};
比较适合于某些操作需要进行遍历整个组织的情况
享元模式
我理解是当一个类型要实例化很多次,但是某个数据成员又是公用的时候,可以通过一个指针和拷贝构造来实例化其他对象,从而达到某些数据成员实现共享的目的。
代理模式和适配器模式
适配器模式本质上就是用老接口来实现新接口
行为相关
迭代器模式
STL中使用了这种,主要是为了遍历容器用的。
策略模式
STL中有些算法会传入一个operator就是使用了策略模式。
状态模式
将状态抽象出来,每种状态实现一次所有接口;然后通过一个函数来切换这个指针的状态,达到实现不同的接口实现。这个像是外观模式的一种延伸。
备忘录模式
保存另一个对象的状态,不需要使用指针。
职责链模式
将一件事情逐步交给下一级,直到这个事情被处理了,使用指针指向下一级而已,其实可以用一个for循环,加上不断修改基类指针指向的对象来实现。
std::vector<Base*> vec_base_ptr;
for(auto p: vec_base_ptr){
if(p->f()){
break;
}
}
观察者模式
观察者和被观察对象,两者之间互相用指针指向对方,因此被观察对象指向观察者的指针要用std::weak_ptr
,而且观察者需要使用std::enable_shared_from_this
。
访问者模式
不通过类型体系的虚函数来实现多态,用pattern matching
方式实现,Refactoring Guru
网站上使用double dispatch
的方式感觉很丑陋。
C++17开始的std::visit模板函数相当于是在编译期帮助匹配了vistor中的某个函数,实现了类似于多对象多态,不需要使用继承来实现多态。
命令模式
常见于GUI相关的MVC中,View和Control交互,Control再和Model交互,View和Control的交换就是通过发送命令,比如Windows的消息队列设计。