设计模式之道


读书摘要

知易行难—设计模式应用的问题

“形而下者谓之器,形而上者谓之道。”—《易经*系辞》
正如许多流行的技术(面向对象、UML等)一样,几乎大部分人都会宣称自己“掌握”、“熟练掌握”、甚至“精通”,然而,真正掌握或者精通的实在是少之又少。

一个典型的现象是:很多人能够熟练背诵出所有的设计模式,能够快速画出各种设计模式的UML类图,也能够熟练地写出《设计模式》一书中各个模式的样例代码。但一到实际的项目实际和开发的时候,往往能够都会陷入迷茫:要么无从下手,不知道哪个地方要用到设计模式;要么生搬硬套,胡乱使用设计模式,将方案和代码搞得一团乱麻。

这是什么原因呢?难道是设计模式不好用,或者设计模式根本就是一个喙头?
答案不在于设计模式本身是否有空,而在于我们没有掌握正确的学习和应用设计模式的方法。

学习《设计模式》一书中的23个设计模式,只是掌握了设计模式的“器”,但并没有掌握设计模式的“道”。就像一个工匠,锯、钻、锤、刨样样精通,但如果不知道在什么地方该用锯,什么地方该用钻,那么他肯定是一个不合格的工匠。为了能够更好地学习和应用设计模式,我们也需要掌握设计模式的“道”。

设计模式的“道”就是用来指导我们什么时候用设计模式,为什么要用设计模式,23个设计模式只是告诉了我们How,而设计模式之道却可以告诉我们Why和Where !

拨云见日–寻找设计模式之道

熟悉《设计模式》一书内容的同学可能会想到:在《设计模式》中,不是每个模式都有“适用性”的说明吗?这个其实就是回答了Where和Why的问题啊!

什么是复杂,什么叫作简单,什么叫作很大依赖性…可能每个人的理解都不一样。

最后,23个模式,所有的“适应性”条款加起来估计有几十条,你能够记住吗?即使能够全部记住,你能够全部理解吗?即使能够全部理解,当你面对一个具体问题的时候,你知道几十条里面哪一条适应你的情况吗?

所以,《设计模式》一书中关于“适用性”的描述,实际上还是太复杂、太多了,不具备很强的实践意义和可操作性。

我们需要的是一个更简单的指导思想,“大道至简”,最好是一两句话就能够描述清楚!

幸运的是,答案竟然就在《设计模式》一书中,但这个答案并不是那么明显!

《设计模式》一书内容的侧重点是23个模式的详细阐述,大部分人可能都是直奔主题,逐一去研究每个模式,而对于本书开头部分第1章和第2章的内容并没有详细研读和思考,或者对这两章只是简单地浏览,并未认真领会和思考,由此错过了最重要的内容。再加上GOF在这两章的内容中,既要引入一个全新的概念,又要提纲挈领地介绍各个模式,还要引入实例进行分析,以至于大量的内容反而将真正核心的内容给淹没或者冲淡了。

设计模式之道就隐藏在“2.6.2 封装实现依赖关系”的最后一段,很简单的一句话:
对变化的概念进行封装(encapsulate the concept that varies)。

你看到这句话可能有点失望,前面分析了那么久,卖了那么多的关子,结果就这么简简单单的一句话,这不是在忽悠吗?
你可千万别小看了这句话,“大道至简”,设计模式之道也不例外,但“简”并不意味着没有;相反,正因为其“简”,每个人的理解才一致,也更好掌握,在实践中才更好应用。正所谓:“真传一句话,假传万卷书”。

GOF在《设计模式》一书中最早提出这个原则,后来不断地有其他专家进行阐述,其中《设计模式精解》(Design Pattern Explained)一书的阐述我认为是最精辟的:Find what varies and encapsulate it. 翻译一下即“找到变化,封装变化”。虽然含义和GOF描述的基本一致,但其更加容易理解。
正所谓:踏破铁鞋无觅处,得来全不费工夫!

庖丁解牛—解析设计模式之道

现在,让我们来深入理解“找到变化,封装变化”的设计模式之道。

首先,“找到变化”解决了“在哪里”使用设计模式的问题,即回答了“Where”的问题

“找到变化”看起来是比较抽象的一句话,但在实践中非常好应用的操作,而且不同领域,不同行业的系统都可以完美地应用这句话。虽然不同领域、不同行业变化的因素、方式、时机等都不一样,但每个领域或者行业的需求分析人员、设计人员,对自己所处行业和领域的可能变化肯定是有比较深刻的理解的,什么会变化、会如何变化、什么时候会变化… …可能都能够自己判断,这种判断并不需要高深的技巧和知识水平,只需要一定的经验积累。

如果我们刚接触一个行业或者领域,经验积累不够,那怎么办呢?是否就无法“找到变化”了?
其实也不然,有一个万能的办法,只是要花费更多的精力了。
这个万能的办法就是“唯一不变的就是变化本身”,也就是说,如果你不知道你什么会变化,那么就抱着怀疑一切的想法,一切就都可能是变化的。
但光有这条指导原则还不行,如果我们真的抱着“一切都是可能变化的”的想法,然后封装一切变化,那么就会陷入变化的漩涡中无法自拔,因为变化是会递归的,A可能变化B,B也可能继续变化,于是这样无穷无尽,系统是不可能做出来的。
所以我们需要一个终止条件,避免陷入无穷无尽的变化递归旋涡中。这个终止条件就是“有限时间内可能发生的变化”。这里的“有限时间”随行业和领域的不同而变化。例如(以下时间仅供参考):
互联网行业可以说,半年内可能发生的变化。。。
电信行业可以说,1年内可能发生的变化。。。
金融业可以说,2年内可能发生的变化。。。
政府行业可以说,3年内可能发生的变化。。。

有了这个指导原则后,你可以这样去问有经验的前辈、大佬、大牛等:xxx在1年内会发生变化吗?会怎么变化?
就这样,即使你是菜鸟,通过这么一招“借花献佛”,也能够轻松发现“变化”的地方。

其次,“封装变化”解决了“为什么”使用设计模式的问题,即回答了“Why”的问题

为什么我们要用设计模式?是因为我们要封装变化!但我们为什么要封装变化呢?
答案很明显:变化不好!
当然这个“不好”不是从业务的角度来说的,而是从系统的角度来说的。从业务的角度来说,“变化”是好的,变化意味着新的机会;但从系统的角度来说,变化并不好,因为变化必然要求系统改动,改动就意味着风险。
虽然变化给系统带来封装,但我们不能因此而“拒绝变化”,因为拒绝变化就意味着失去了机会。简单来说,赚不到钱的系统,设计再优美,功能再强大,系统再稳定,也不过是一堆无用的摆设:

客户给你提了新需求,你不做,能拿到合同吗。。。。。。
行业正在兴起新的流行功能,你不做,你的系统有人用吗。。。。。。
一项创新带来了新的机遇,你不做,能抢占市场吗?。。。。。。
因此,我们要“拥抱变化”,但又不能让变化带来太大的风险,所以就提出了“封装变化”。“封装变化”意味着将变化的影响控制到最小,将风险降到最低。
我们来看看变化会带来哪些问题和风险。
开发人员需要编码以适应变化,设计不好的方案将导致大量的编码工作量、自测工作量;
测试人员不单要测试因变化而新增的部分,还要测试受影响的部分,设计不好的方案,牵一发而动全身,导致测试工作量大大增加;
如果为了适应某个变化而对系统做了比较大的改动,则系统的质量风险将上升,很可能导致上线失败,或者上线后出现各种问题。

因此,我们要尽量减少变化带来的工作量和风险,而减少的最有效方法就是将变化的影响范围缩小,即:将变化封装起来,使其只在有限的范围内有影响。

举一反三—活学活用设计模式之道

就像一个武林高手有了深厚的内功,天下万物皆可成为手中的利器,而不必拘泥于具体的武器和招数一样,掌握了设计模式之道后,我们其实也完全可以不拘泥于只使用《设计模式》一书中的23个设计模式,可以根据需要选择最合适的方案。
例如:
不同的业务有不同的规则排列组合,规则引擎可以封装各种变化的规则。。。。。。
类之间的依赖是变化的,Spring使用XML配置文件来封装这种变化。。。。。。
每个银行的卡都不一样,银联封装了这种变化,使得不停银行可以互通。。。。。。
总之,你可以使用类和设计模式来封装变化,也可以使用配置文件和模块来封装变化,还可以使用一个系统来封装变化。。。。。。

原则 VS 模式

在“设计模型”一章中,我们提到设计原则和设计模式是互补的,设计原则和设计模式互补体现在:设计原则主要用于指导“类的定义”的设计;而设计模式主要用于指导“类的行为”的设计。

如何应用设计原则

经过前面深入的阐述,SOLID原则我们基本上讲清楚了,但如果想熟练地应用SOLID原则,不仅仅指导SOLID是什么(what)还不够,还需要指导SOLID原则在什么时候和什么场景(when和where)应用。
幸运的是,SOLID原则的5个独立原则在实际应用中基本上都是独当一面的,并不会在某个地方需要同时从可选的几个原则中挑选一个最优的原则来应用,这样大大降低了我们应用SOLID原则的难度。
SOLID原则的具体应用场景如下。

SRP原则:用于类的设计

当我们想出一个类,或者设计出一个类的原型后,使用SRP原则核对类的设计是否符合SRP要求。

OCP原则:总的指导思想

OCP原则是一个总的指导思想,在面线对象的设计中,如果能够符合LSP/ISP/DIP原则,一般情况下就能够符合OCP原则了。
除了在面向对象的软件设计中应用外,OCP也可以用于指导系统架构设计,例如常见的CORBA、COM协议,其实都可以认为是OCP原则的具体应用和实现。

LSP原则:用于指导类继承的设计

当我们设计类之间的继承关系时,使用LSP原则来判断这种继承关系是否符合LSP要求。

ISP原则:用于指导接口的设计

ISP原则可以认为是SRP原则的一个变种,本质上和SRP的思想是一样的。SRP用于指导类的设计,而ISP用于指导接口的设计。

DIP原则:用于指导如何抽象

当我们设计类之间的依赖关系时,可以使用DIP原则来判断这种依赖是否符合DIP原则。DIP原则和LSP原则相辅相成:DIP原则用于指导抽象出接口或者抽象类,而LSP原则用于指导从接口或者抽象类派生出新的子类。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值