今天看了一本Gof的23钟设计模式的书(PDF版本)。
这本书《设计模式精解-GoF 23 种设计模式解析》已经比较精简了。
本文相当于是笔记。
1.Factory 模式
1.1 多子类创建整合
factory模式的这种功能是可以将多个子类的创建整合在一起。这样使用者不需要关心创建的实现。你不需要知道他们到底是使用了不同的子类来实现还是用参数标记来实现。
factory模式的另一种功能是,可以用过不同的方法名来创建不同的对象,同样起到隐藏实现的功能。
实际上这种封装方式就像是对代码的一种整合。比较2b的说法是,这样比较好看。当然,他不可能真的仅仅只是好看。在后续使用和维护的过程中,使用factory模式会让你的工作量减少N倍,并且更不容易出错。
1.1.1举个例子
有N个子类,在不使用factory的情况下,遇到需要什么子类,就直接创建这个子类。这样看上去视乎逻辑清晰且简单。
但是一旦需要更新,比如将N种子类变成N/2种(我们发现有两个大类其实是可以整合在一起的)。这个时候如果在M个地方创建了这种类型,就得更改M次(不要说使用全文替换的方式,这种方式可能会产生更大的隐患。你以为他正常工作了,但是他可能会过多的替换,或者漏替换)。而使用factory模式,只需要更改factory即可。哪怕更改代码的数量是一样多的,factory模式也有他的优点:你可以很明确的知道要改哪里!
1.2 延后创建
factory模式还可以将创建动作延后,我们可以在创建factory时就将参数某些参数确定。然后在需要的时候进行创建。这样可以隐藏在此之前的具体初始化动作。你不需要知道在创建之前还有哪些初始化动作。
1.2.1举个例子
如果要召唤一个召唤生物,在创建的时候,你只想关心他的生存时间,而不关心召唤的是猪还是鹰。我们可以在一开始就将需要的factory保存下载,当需要的时候直接创建并传递需要的参数。
当然,你可以将所有与这个召唤物相关的参数都保存下来,然后在需要的时候创建。如果我们对这种方式进行优化,将这些参数封装到一个对象中。实际上这就是比较原始的factory了。factory只是把一些可以在更早之前就确定的东西初始化。这样可以避免每次都做重复的动作(比如每次都通过type switch到相应的子类)。
2 AbstactFactory 模式
AbstactFactory更像是Factory模式对FactoryObject的一种应用。只不过factory中一般只会用于创建一种类,而AbstactFactory会创建多种类。所以将AbstactFactory作为一种新的模式有些多余,算是factory的一种升级
3.Singleton 模式
单例模式的核心理念就是该类只有一个对象。除此之外的所有问题都不是问题。
比如说我能不能使用该类类型的静态成员变量,能不能使用全局变量,能不能让该类的所有变量和方法都变成静态的来实现这一需求。
能!
实际上单例模式就是使用静态成员变量的方式实现的。
只要你觉得你实现的方式更好用,并且实现了单例模式的理念。那么就是一种单例模式的实现。
一般情况下我们会将Factory和Singleton结合。
实际上只要是满足:该类只有一个对象的。都应该使用单例模式来实现。
3.1 优势
单例模式会尽可能从根本上解决类有实例化多次的问题(通过破坏单例的原则,可以让单例实例化多次,但这种操作不可能是意外产生)。
单例互相之间调用时,你不需要关心初始化顺序的问题(当然单例不能解决初始化时循环调用的问题,单例A初始化是需要单例B,单例B初始化是间接需要单例A)。
单例在实现的过程中更自然(更自然的实现更容易理解,也更不易出错)。
4.Builder 模式
Builder模式算是一种非常弱的模式(只是小小的技巧)。
Builder模式让我想起小时看的一个动画片。具体不清楚了,不过有个情景记忆深刻。
“组成腿和脚,组成躯干和手臂,我来组成头部。。。”
Builder模式大致也是这个意思。将一个Object的初始化分成了几个部分。可以在不同的时间,不同的地方,甚至是不同的人来实现他的某一部分的初始化。当所有部分都初始化后,就可以获取到这个Object了。
5.Prototype 模式
为类提供一个复制自身的方法。这样当你那当一个基类对象Clone的时候就不需要知道这个基类对象实际上是那个子类的对象。
6. Bridge 模式
桥接模式将多用组合类型连接在一起。如果我们要支持N种平台,M种实现。如果直接使用子类的方式你将产生M*N个子类。当然,在实际的开发过程中Bridge模式的使用是自然而然的。当发现没增加一个类的实现是需要增加N个子类时,很自然的你就会想到使用一个新的类,来整合多层变化。
7.Adapter 模式
接口封装模式,将多种不同实现方式的接口封装成一个统一的接口。可以隐藏底层调用。多用于跨平台(不同平台的线程机制不一样)和第三方库(第三方的调用风格和现有的风格不一致)调用。
8.Decorator 模式
装饰器模式,允许动态添加装饰器,用来对某一方法的加强。比如我们对一断文本进行加密时,我们可以饿使用多种装饰器(加密方式)对其进行加密。当然在解密的时候需要是逆序的。
9.Composite 模式
组合模式。隐藏结构特征。你不需要知道该节点是叶子节点还是非叶子节点。举个例子,给一个人发工资或者给一个部门发工资,还是给一个小组发工资。对于发工资的人是不在乎拿钱的是人还是部门或是小组,他只想知道要发多少钱。至于内部真么发他就不管了。
10.Flyweight 模式
共享模式,将一些会被多个地方使用的共有的数据作为一个共享的Object。比如有很多颗圣诞树(里面的树是完全一样的,诡异的很),我们将他摆放在不同的地方,房间里走廊里面,房子外面。放在不同地方的用不同的装饰物品。这个时候对树本身的描述是一致的,但是他的位置和装饰物不一致。
不得不说的是,一般情况下,我们还需要一个object来管理这些共享的Object(不然你不知道是要new一个出来,还是使用已有的)。而这个Object一般就是一个单例。同时这个Manager还可以是一个factory。
11.Facade 模式
外观模式。将子系统中的一组操作封装成一个API。(是个人都知道会这么做啊,还专门搞个设计模式,装逼装到家了)
12.Proxy 模式
HttpProxy大家应该不会陌生。实际上代理是为了隐藏调用者,而反向代理才是为了隐藏实现者。而设计模式中的Proxy模式更多的是隐藏实现者。所以这个地方叫反向代理模式比较好。
13.Template 模式
模版模式,将基础的逻辑实现隐藏起来。这个模式应该是使用最为频繁的了。std::vector,std::list,std::map,std::set。就算没用过也肯定听过吧。
14.Strategy 模式
策略模式。将功能的多样性封装成object。以便在运行的过程中进行组装。
15.State 模式
状态模式。状态机知道不,不同的状态下使用不用的实现。
16.Observer 模式
OB模式,监听某些事件,并做出相应的处理。监听者应该允许被动态的添加活删除。
17.Memento 模式
备忘录模式。该模式允许将一个Object的当前状态保存下来,并可以通过这个保存的Object对其状态进行恢复。(这是一个想当然的设计模式,我们当然想在任何时候都反悔上一次操作,为了实现这一功能,在极端的情况下,你的内存资源消耗需要*2)。
18.Mediator 模式
中介者模式,将调用和实现双方都隐藏。对于通讯模块有相当好的效果。
19.Command 模式
命令模式,将参数和参数明隐藏起来。
20.Visitor 模式
访问者模式,将一个数据Object的操作封装起来。这样当你更新操作方式的时候可以和原有数据无关。
21.Chain of Responsibility 模式
责任链模式,处理一个事件,或者(实际上也可以及处理还传递)将其传递给后续的节点处理。
22.Iterator 模式
你可以去用用std::list,std::map,std::vector,std::set。迭代模式就是实现迭代器功能。
23.Interpreter 模式
解释器模式。解析脚本。每一个解释器的实现,都是一种新的脚本语言的发明(是不是很屌)。
24 后记
其实设计模式只是一些编程的技巧。不是说一定要使用某种设计模式。但是当你被某个问题所烦恼或者你预期某种麻烦的时候,你所想的解决方案大致也会趋向与现有的设计模式。所以你可以熟练的掌握这些模式,当你遇到问题的时候使用这些模式的核心思想。最后产生一种解决方案,或者是和某种模式基本一致,或是几种模式的结合体,或是再参杂一些自己的理解,这些结果都是极好的。设计模式更像是前人总结的经验,他可能已经不再适用于现在的环境,但是了解他的原理说不定会对你创建一种新的模式给与灵感!