设计模式的运用
前面三篇文章对23个GOF设计模式作了简单介绍,并且对设计模式的思想做了初步的探究。接下来的这篇文章作为整个设计模式之旅的最后一篇,讲讲在实际设计或者开发中怎么运用设计模式及其思想。
学设计模式的目的是为了用,但是应该怎么用?这是一个问题,还是一个大问题!难道是拿着23个设计模式一个个比对,看哪一个更贴切?如果是这样,我想,设计模式不学也罢。况且,如果遇到23个设计模式都不适合的情况,该怎么办呢?
在回答“用”这个问题的时候,我觉得需要从三方面入手:
1, 什么时候用
2, 在哪用
3, 怎么用
这篇文章的主要内容也是回答这3个问题的。
什么时候用
设计模式研究的是类和对象,肯定是在详细设计及编码阶段使用,不过我需要把这个过程再细分一下,这样就更明确。在这个阶段,需求已经知晓了,每个开发人员被分配了不同的模块,针对自己的模块,接下来的过程大概是这样:
1, 评估难点,想好怎么做
2, 定义类和接口(详细设计)
3, 实现(编码)
4, 扩充或者修改接口
其中3和4是循环往复的,3到4,4之后,又到3,因为接口都变了,肯定需要编码实现。
对于2,有两种方式,一种是全盘设计;另一种是忽略2或者只设计一部分就进入3,边写边添加接口或者类。我一般都是不由自主地采用第二种,在写小模块或者非常独立的模块的时候还OK,但是大模块也这样写的话,代码的确很烂。并且这样的话,大脑中对代码没有整体印象,都是局部思维,顾此失彼,Bug也很多。而对于第一种,虽然前期花了一定时间,但是非常值得,一方面可以在前期发现一些问题、提前解决;另一方面,对模块有整体把握,各部分彼此怎么互动做到心中有数。如果要用设计模式的话,全盘设计是很好的一个时间点,不然就没有设计模式的用武之地,因为你从来都是在编码,没有时间思考怎么设计,而详细设计就是给你时间。经常在网上听说“码农”什么的,说实话,我不怎么喜欢这个词,如果说你能把设计模式运用好,至少有了全局思维,就是常说的“大局观”,我想一般的“农民”可没有这个。
3就不用说了。为什么有4呢,因为我不相信谁能一开始就把什么都想到。4的存在有两个原因,一是可能和其他模块冲突,后来要互相协调;另一方面可能需要适应某个场景,这个场景一般不出现,也很难预料到。面对接口的改动,我一般采取的方法就是直接修改,现在学习了设计模式,就提供了另一种途径来达到目的,比如说采用适配,装饰啊等等。所以这个时候,也需要用设计模式的思想来思考。
在哪用
设计模式的运用不仅是23个具体设计模式的使用,更应该是设计模式思想的运用。所以在设计类和接口的时候,总应该按照设计模式思想来设计,而不是说这一模块用设计模式思想来思考,另一模块则不用。需要说明的是按照设计模式思想来设计,并不是说只按照它来设计,毕竟设计模式只是为了解决复用而生,在真实环境下,可能还需要考量其他因素比如:性能、可用性等等。但是设计模式思想或者复用作为一个因素需要被全盘考量。
怎么用
回答“怎么用”这个问题涉及两个方面:
1, 步骤是什么样的。
2, 注意事项,也就是可能遇到的问题及解决办法。
首先我们来说说类设计的步骤,我不说具体步骤是什么,因为类设计大家都会,我只强调一下顺序,应该是先抽象,后具体。这样才有可能做到具体依赖抽象,因为抽象的东西相对稳定,即使具体的再变,整个设计变化也不是毁灭性的。
其次说说注意事项,这个注意事项其实就是设计模式的思想或者原则。我在这里也不会把设计模式的思想再说一遍,因为在前面的文章里已经说过。我这里只关心这些思想运用时出现的两个问题:
1, 什么时候可以不用接口?
2, 什么时候需要隔离?
我们先来看看什么时候可以不用接口。在设计模式中是鼓励使用接口的,主要有两个目的,第一个是接口隔离变化,另一个是为了抽象。但是如果定义每一个类之前都需要一个接口对应,一方面增加工作量,另一方面也没必要。比如有一个类专门用于记录数据,这个类定义接口有什么用呢,徒增烦恼而已。所以在模块内部,满足以下条件的实体可以不定义接口:
1, 不和外部交互,不需要隔离变化;
2, 只有数据,或者行为固定不变。
接下来,我们来看看什么时候需要隔离,简单分为两种情况:
1, 模块之间需要隔离。
2, 模块内部,如果一方可能有变化就需要隔离。
对于模块间隔离来说,如果是接口隔离,接口定义的位置也需要注意。这些接口最好统一定义在一个模块内。比如说模块a和b交互,需要接口i1和i2,那么i1和i2要么都定义在a内,要么都定义在b内,这样就不会造成互相依赖。还有一种方式就是把i1和i2定义在模块c,这样a和b都依赖c。