一、
引子
记得一年前,我开始陆陆续续在自己的
blog
上连载《深入浅出设计模式》。其内容无出经典巨著《设计模式》之右,仅仅偶有己见,但是它记录了我学习、思考和讲述设计模式的过程。一晃,距离写成最后一片设计模式的文章已有
3
月余,我却迟迟没有对设计模式做一个总结。心想,总不能虎头蛇尾吧,于是便有了这篇文章。
二、
回顾
23
种设计模式
先来回顾下这
23
种经典的设计模式吧,下图给出了
GOF
对它们的分类:
图中从两个纬度将
23
种设计模式划分为六大类:创建型类模式、创建型对象模式、结构型类模式、结构型对象模式、行为型类模式、行为型对象模式。
GOF
对这
23
种模式的划分是有一定道理的,虽然人为的类型划分,说到底还是有些牵强,但是如
GOF
所说,它至少可以帮助记忆学习。
被分为六大块的
23
种设计模式并不是割裂开来的,很多模式的使用往往是相生相伴的,像工厂与单例,装饰与组合等等。
GOF
给出了模式间的关系详细描述如下图:
记得曾经不止一次有人问我:这模式和那模式感觉上一样啊,有什么区别啊。同样,在很多论坛上也充满了这样的疑问。其实这是很正常的,面向对象设计、编程所能使用的方式不外乎这几种:继承、组合、封装行为、利用多态等等,所以
23
种模式中翻来覆去的使用这几种方式,看起来当然是似曾相逢。有人曾留言给我,让我着重表述这些模式之间的区别与类似。我当时也许诺会在最后写一篇总结性的文章专门讨论这个话题,但是现在我不打算这样干了。
三、俯瞰全局、
追踪溯源
什么是设计模式?
GOF
在书中如是说:
设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描述;
John Vlissides
曾说过:在设计模式中,仅有的、最重要的就是不断的反省;而我将它比作软件开发中经验积累出来的“公式”。
通看这
23
种模式,就应了
Dennis DeBruler
曾说过的一句话:计算机科学是一门相信所有问题都可以通过多一个间接层(
indirection
)来解决的科学。在前面关于具体模式的文章中,我曾经不只一次的提到“中间层”。但是直到读到这句话,才使我跳出具体模式的束缚俯瞰全局。
23
种模式似乎不再神奇,它们在解决问题上的思路是如此的相似——添加间接层。何止模式如此,正如
Dennis DeBruler
所言,我们所接触的很多技术都是采用这种手段,如:
J2EE
的分层技术、重构等等。但是在具体操作上,
23
种模式中间接层的应用可大可小,无比灵活。观察者模式在动作触发端与动作执行端之间加入了目标角色层,解除了两端之间的耦合,巧妙地解决了一对多的关系;单例模式将构造方法私有化,并在使用者与构造方法之间添加一个获得唯一实例的静态方法,达到控制实例个数的目的。
间接层应用如此广泛,得益于它能带来如下好处:共享逻辑(重用)、分离意图和实现(提高灵活性)、
隔
离变化(封装)、解耦等等。既然我们知道了间接层这么一回事,似乎我们可以不用知道设计模式也能做出像设计模式那样好的设计来。但是要记住,间接层应用过
于泛滥,则会过犹不及,它会导致简单问题复杂化、跳跃阅读难以理解等问题。那如何在设计中把握使用间接层的度呢?设计模式也许是很好的范例——你毕竟是站
在了巨人的肩上。
再深入一层细看,在设计模式中,广泛遵循了两条设计原则:面向接口编程,而不是实现;优先使用组合,而不是继承。这两条原则带来的好处,自然不用再说了。说到设计原则,
现在为人熟知的设计原则有很多,如:
单一职责原则(
SRP
)、开闭原则(
OCP
)、
Liskov
替换原则(
LSP
)、依赖倒置原则(
DIP
)和接口隔离原则(
ISP
)等等。这些原则的出现大多都早于设计模式,但同设计模式一样,是
OOD
过程中经验积累的结晶。它们在
23
种设计模式中都有体现,因此了解设计原则可以帮助你更好的分析和理解设计模式。
当然,这并不是说设计模式就是建立在设计原则的基础之上的。两者之间的关系是互相促进的。设计原则的诞生,也许会促成新的设计模式;设计模式的出现,也许会提炼出新的设计原则。
现在不禁要问,为什么使用设计模式。也许你的回答会是:提高设计的重用度、灵活性、可维护性等等。但是我认为更准确的回答应该是:解决系统设计中现有的问题。这便又回到了
GOF
给设计模式下的定义上了,绕了一个圈子,原来答案就这么简单。
这也就是我不想详细讲解各种模式区别的原因了,要解决的问题不一样就是它们之间最大的不同。如果还要详细分析,那它们都已写在
GOF
巨著中了——就是它们的定义、使用范围、优缺点等等。
四、
活学活用
是否我们必须按照这本巨著上描述的形式来使用设计模式呢?肯定不是这样的。数学物理公式在不同的条件下还会有不同的衍生式,何况这些在实践中总结的经验呢。
Martin
建
议根据问题的复杂性逐步的引入设计模式。这是个很好的建议,它避免你套用模式而带来了过度的设计,而这些过度的设计也许直到最后都派不上用场。比如,你的
系统中现在就仅有一个适配器角色,或者各个适配器角色没有什么共性,那么目标角色和适配器角色就可以合为一个,这样使得设计模式更加符合你系统的特性。
不
仅如此,做到设计模式的活学活用,我认为还要做到以解决问题为中心,将设计模式融合使用,避免为了设计而模式。当然这是建立在对各种设计模式了如指掌的情
况下。比如,有一段解析字符串的对象,而在使用它之前,还要做一些参数的判断等其他非解析操作,这时,你很快就会想起使用代理模式;但是这个代理提供的接
口可能有两三个(满足不同情形),而解析对象仅有一个接口,命名还不相同,这时你又想到了适配器模式。这时也许将两者融合成你独有的解决方案比笨拙的套用
两个模式好的多得多。
五、现在看模式
GOF
说过,这
23
种模式仅仅是对一般设计问题的总结。现在许多专有领域也开始出现了大量的设计模式,至少在我最了解的企业应用这个方向是这样的。其中有一部分模式仅仅是对
GOF
设计模式的再次包装。我们不妨叫
GOF
的
23
种设计模式为原始设计模式。
但是遗憾的是,越来越方便的框架支持,领域模型简化造成代码过程化、脚本化,使得在企业应用中很难看到原始设计模式的影子(当然还是可以看到遍地的命令模式)。比如:
IoC
容器将单例、工厂、原型模式包装了起来,你现在需要做的仅仅是填写配置文件;框架集成了观察者、模版等等模式,你仅仅按照框架说明实现具体对象就可以了;过程化、脚本化的代码里面更不要提什么设计模式了!更甚者在
EJB
中单例模式差点变成了反模式。
原
始的设计模式没有用,过时了吗?如果你不甘心仅仅做一名代码组装工;想对你们部门的高手设计的框架品头论足的话,答案就是否定的。原始设计模式有很多的确
是难得一见了,但是了解它们绝对不是在浪费你的时间,它可以让你在解决问题的时候思路更开阔一些——它的思想比它的架势更重要。
写在最后(其实应该在最前面)
细想自己在学习设计模式时,常常埋怨《
Java
与模式》肤浅无物,为了模式而模式;又感叹
GOF
写
得高度概括,苦于理解。于是便有了将自己对设计模式的认识写下来的想法。正巧参与了部门组织的一次设计模式讲座,触发了第一篇文章的诞生。从现在来看,文
章倒是全写成了,可内容上却不能让自己满意,却又懒得动手修改(谈何容易)。“深入”二字说来容易,做到何其难,自以为这些文章的分量远远够不上;倒是
“浅出”,自以为还可以沾上点边。你可以把本系列文章看作是《
Java
与模式》的替代品,帮你叩开设计模式之门。如果你要深入研究设计模式,我劝你还是去研读《设计模式》一书吧。