1、背景:
1994年,软件设计领域的四位大师(GoF,“四人帮”,又称 Gang of Four,即 Erich Gamma, Richard Helm, Ralph Johnson & John Vlissides)通过论著《Design Patterns: Elements of Reusable Object-Oriented Software》阐述了设计模式领域的开创性成果,其在这本书中总结了23个经典的设计模式。
20多年过去了,软件行业迅猛发展,越来越多的新模式不断诞生并得以应用,但作为设计模式的根基,深刻理解和学习23种设计模式仍是非常有必要的,本文就择其重点来重温这23个经典的设计模式,并结合C++语言本身给出一些实例。
2、分类:
书中将23种设计模分成以下三类:
创建型模式(Creational Pattern):
关注类和对象的创建过程。
对类的实例化过程进行了抽象,将模块中对象的创建和对象的使用分离。
使用者只对接口编程,掩盖实现细节,使整个系统的设计更加符合单一职责原则。
结构型模式(Structural Pattern):
关注类和对象的组合过程,从而形成更加强大的结构。
又可以细分成类结构型模式和对象结构型模式:
-
类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系。
-
对象结构型模式关心类与对象的组合,通过关联关系使得在一 个类中定义另一个类的实例对象,然后通过该对象调用其方法。 在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。
行为型模式(Behavioral Pattern):
关注类和对象的交互过程,重点在相互作用上。
3、分类:
23种设计模式都是针对于具体场景提出的,是为了解决具体的问题,所以无论是在学习还是在具体使用时,最好能够熟记一两个使用场景,这样用起来才能更加融会贯通。
所有的设计模式都是为了代码服务的,切忌为了使用而使用的过度设计,也切忌纠结于使用具体哪一种设计模式而束手束脚,只要能够使代码结构清晰,满足“高内聚,低耦合,高扩展”的设计风格,都是好的设计。
下面,我们根据使用场景的不同,将这23类分成不同的部分,因为有些设计模式的场景太过单一,难以应用到其他场景,比如备忘录,命令等。而有些设计模式比较简单易懂,比如工厂,观察者,迭代器,我们也不做过多讲解,下文只重点关注其中的部分设计模式,其他的部分请读者自行查阅。
创建型 | 结构型 | 行为型 | |||
常用 | 不常用 | 常用 | 不常用 | 常用 | 不常用 |
单例(Singleton ) 工厂(Factory) 抽象工厂(Abstract Factory) 建造者(Builder) |
原型(Prototype ) | 代理(Proxy ) 桥接(Bridge) 装饰者(Decorator ) 适配器(Adapter ) |
外观(Facade ) 组合(Composite ) 享元(Flyweight) |
观察者 (Observer ) 模板(Template ) 策略(Strategy ) 职责链(Chain of Responsibility) 迭代器(Iterator ) 状态(State) |
访问者(Visitor ) 备忘录(Memento ) 命令(Command ) 解释器(Interpreter ) 中介(Mediator ) |
4、创建型模式(Creational Pattern):
顾名思义,创建型模式(Creational Pattern)就是着眼于如何创建对象的模式
创建型模式对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。
4_1、单例模式:
要谨慎的使用单例模式。
因为在面向对象中使用单例和在面向过程中使用全局变量是一样的,也就意味着所有使用Singleton的客户端和其是紧耦合的,这样可能导致的结果是:
- 失去了多态特性,比如难以使用Mock Object来完成单元测试。
- 如果单例类要更改使用方式,那么所有的依赖类都需要更改。
所以,本文建议在工程中,只把那些工具类声明为单例,比如计算时间的类或者字符串操作的类等,而对于实体类,推荐使用依赖注入(Dependency Injection)来代替单例实现。
现以调用日志库为例:
使用单例模式实现的Logger类如图所示:
#include <string_view>
class Logger {
public:
static Logger& getInstance() {
static Logger theLogger { };
return theLogger;
}
void writeInfoEntry(std::string_view entry) {
// ...