[TICPP] 设计模式学习笔记

目录

《C++编程思想》第二卷第十章设计模式 学习笔记

设计模式算是高级技巧,其实一般也用不太多,就像《极限编程》里说的,简单才是王道。不过,学习设计模式也挺有用的。前段时间,我改写之前写的一段程序,增加一些功能,改得我头破血流,差点儿完全重写,程序结构简直惨不忍睹,没有一点儿的扩展性,所有代码基本都是紧密地耦合在一起,每增加一个功能,就得从头改到尾。学点儿设计模式,一定会有用的。
一次听同学说,各种设计模式中,用得最多的也就是单件模式和工厂模式。或许,策略模式也有用,需要使用不同种的算法的情况还是挺多的。其它的模式,我还没用过,以后参与大项目制作很可能遇到吧。

单件模式

允许类只有一个实例。可用类的静态成员变量实现,也可以用类静态函数中的静态变量实现。
第一种方法中可使用惰性初始化策略,在第一次使用时初始化。此时,成员变量为指针,并需要处理析构函数。
还有一种实现形式,利用特殊的递归模板形式,参见书中说明。

命令模式

在一个对象中将一系列命令封装起来(比如用vector),再执行。可以向该对象传递函数或想要的动作。
一个例子:令Command为命令基类,则Macro中用vector<command *>收集一系列命令,再执行。

消除对象耦合

新创建一个类作为代理,而将实际工作的类隐藏起来。可用于匹配类的接口。区别是:
代理只有一个实现类;
状态模式可以有多个实现类,并可以任意切换。

代理模式

将具体的实现类作为私有成员(指向实现类的指针),而编写额外的接口。实现类在整个过程中不变。

状态模式

与上面相似,但在执行过程中可以改变实现类(delete掉原来的实现类,再new一个新的实现类)。

适配器模式

接受一种类型,并且提供一个对其它类型的接口。
用于将两个接口不同的类匹配在一起。最典型的例子,为了调用STL算法,而对自己的类编写迭代器。

模板方法模式

使用继承。保留相同的代码,而覆盖掉有变化的代码。

策略模式

使用组合。与模板方法模式有些类似,但更灵活。可以在运行时控制策略的选择和使用,与状态模式比较像。
content类控制策略,其中的策略可以是类中的成员变量,保存策略的引用,当然也可以是指针之类的。

职责链模式

尝试采用一系列策略模式,直到找到满足条件的那个策略。
例子:
Strategy为策略基类,一系列派生类作为具体策略。职责链也从Strategy派生出,包含vector<strategy*>成员变量,在调用中遍历vector,直到找到应该调用的那个策略。

工厂模式

最重要的设计模式之一。将创建对象的代码转到这个工厂来执行,这样在新增加类型后只需要修改这个工厂就可以了。工厂模式有多种变形。

  1. 先介绍一种简单的工厂模式:
    在基类中定义一个静态成员函数,该函数负责生成各种派生类对象。因而,定义这个函数需要在所有派生类对象的后面。
    派生类的构造函数是私有的,且需要把基类作为派生类的友元。
    静态的工厂函数根据一定的条件生成相应的对象,字符串参数是个不错的选择,函数体中不可避免地出现一系列if-else判断,或者干脆用整型变量,使用switch判断。
    这种结构有缺点,基类需要知道派生类的信息,加入新结构,需要修改基类。我们的原则是,尽量增加,而不改变原有代码。这与面向对象思想有冲突。当然,这是最简单的形式。
  2. 多态工厂
    使不同的工厂派生于工厂基类,与上面的单一工厂不同。来个例子:
    新的工厂Factory作为基类,其中用map<string, Factory*> factories静态成员变量来封装一系列具体工厂。某个静态成员函数根据string参数值,调用map中的具体工厂对象来创建具体对象。
    关键在于如何填充factories成员变量,静态成员变量应该避免重复赋值,只一次操作就够了。可以通过一个单件模式的初始化器FactoryInitializer类来添加各个具体工厂。每次增加新的对象,需要修改这个初始化器。
    这种方法比较复杂,简单应用就用第一种方法就好。
  3. 抽象工厂
    使用若干工厂方法模式。每个工厂模式创建一个不同类型的对象。当创建工厂对象时,需要选择由哪个工厂来创建所有对象。主要用于不同类由几个部件构成,而每个工厂决定每个部件的种类,就像是做同一个产品,但有不同的规格。
    部件和工厂都有抽象基类,最终的产品包含各个部件及具体工厂(这关系有点儿别扭,书上就是两个部件指针和一个工厂指针)。
  4. 虚构造函数
    显然这是模拟的效果,构造函数不是虚函数(虚函数起不到作用?)。
    模拟虚函数,在基类的构造函数中实现,根据字符串参数,将基类Shape中的Shape* s成员变量创建为相应类型。基类中的接口虚构函数中,都调用s的相应函数,在此处实现动态绑定。
    这方法太复杂了,真的有人在用么。

构建器模式

用于创建复杂对象。
将对象的创建和它的表示分开。对象有多部分组成,每个创建器创造一个具有特定部分的对象。创建对象时,选择不同的创建器,就会得到不同表示的对象。
Part是对象单个成分的基类。Product由多个Part组成,可用vector<part*>表示,或者用map。
Builder是创建器的基类,包含一个Product指针,通过一类列步骤函数向Product的vector<part*>中放入特定的Part。并包括动态创建product对象的方法,和返回product对象的方法。需要另外的对象调用Builder来组装对象,这个类就是Technician。
Technician通过传递给它的具体Builder,组装相应的对象。组装后,Builder中的Product指针指向的产品完全建立好,通过Builder的getProduct函数返回具体产品。

观察者模式

一个对象改变状态,如何更新一组其它的对象。
有两个角色,观察者(Observer)和被观察者(Observable)。当被观察者状态改变时,调用notifyObservers()通知观察该对象的所有观察者(在被观察者维护的列表中),调用观察者的update函数。
可以动态添加删除某个对象上的观察者。更进一步,对象上可以有多个种类的观察者,每个种类的观察者作为一个列表出现,并控制这些观察者的是否活动。这种情况可用类似于Java中的 “内部类”实现,不同种类的观察者由具体Observer类中的Observalbe派生类区分,每个种类维护各自的观察者列表,并有外层类的指针(parent指针),用于获取外层对象的信息。
可以将几个观察者组合成一个类,以内部类的形式来定义各个观察者。

多重派遣

确定多个不知道类型之间操作。
只介绍双重指派。
基类Item中有两种虚函数,一种只有一个函数,用于第一次指派,参数为基类指针Item*,调用第二个虚函数。第二种虚函数用于第二次指派,参数为具体类,为每个具体类型编写重载版本,并在派生类的相应版本中完成操作。

访问者模式

这东西太复杂了,没看,直接略过。
 
以上是几种模式的简介,有个最重要的原则没说:组合由于继承。
记住一句话:只要能用,就做最简单的。