提到设计模式,有一个非常有意思的现象: 理论学习中,几乎所有的开发人员都认为它非常有用很重要。 工作实践中,绝大部分开发人员在项目中找不到合适的应用场景。 设计模式学了一遍又一遍,却毫无用武之地。大概设计模式最好的归宿,就是存在程序员的深深的脑海里。
难道设计模式真的没有用了吗?
关于本文
本文目的,通过对设计模式的本质进行探讨剖析,建立起更为高效的认知模式。最终可以灵活运用设计模式到日常工作中,产出稳定、高效、灵活的业务实现。
内容上分为上下两篇,上篇为理论篇,讲述了设计模式一些共识、原则的思考理解等。下篇为实践篇,通过一个完整的系统设计实例,对上篇的理论进行验证和实践。
从因果规律来看,任何事物的发展与诞生,必然有其背景原因。想要摸清设计模式的底细,就要明白设计模式是怎么来的。
时间回到20世纪80年代,当时的软件行业正处于第二次软件危机中。根本原因是,随着软件规模和复杂度的快速增长,如何高效高质的构建和维护这样大规模的软件成为了一大难题。
软件复用被认为是解决这一危机的一条可行路径,而面向对象的思想则很好的解决了复用问题。设计模式正是在这样的背景下,伴随着面向对象编程的兴起出现的。
设计模式这个术语,其实并非源自于软件工程领域。它最早起源于建筑学领域,是由哈佛建筑学博士Alexander在1977年提出的。
他跟团队在研究了大量的建筑结构后发现,为了解决同一类建筑问题而设计出的高质量建筑结构具有某些共性,于是使用“模式语言”来描述它。
他定义每个模式均包含前提条件(适用场景)、目标问题、解决方案三个部分。
创建模式的目的是,复用那些已经实践成功的建筑解决方案。
1994年,以四人组(GoF)自称的四位资深软件工程学者,借鉴了这种思想,将模式的概念引入软件工程领域。GoF总结了23种在开发过程中使用频率较高的设计模式,合著出版了《Design Patterns - Elements of Reusable Object-Oriented Software》一书。设计模式正式诞生。
软件领域的设计模式,本质上是基于面向对象设计经验的总结。 它不是解决所有的问题的银弹,更多的是一种解决问题的思路。
简单了解了设计模式后,对于文章开头的问题,你有答案了吗?设计模式真的没有用了吗?我们可以提出一些合理的猜测:
设计模式所代表的经验已经过时了。 距设计模式的提出已经过去了20多年,软件领域已经有了长足的发展,有可能经验已经不适用了,所以日常开发中运用设计模式的场景比较少。
业务软件开发不需要设计模式。 随着软件领域的发展,软件开发的分工,慢慢朝着细致化、精致化方向靠拢,技术本身开始独立于业务发展。我们用上了各种各样的功能强大的工具框架,填好模板代码就能完成业务,似乎业务已经不需要过多设计了。
接下来,我们围绕这两个问题一起讨论下。
不妨先来回忆一下,我们接触到设计模式最多的地方是在哪里?
大部分应该都是在框架(工具类库等)源码里吧。那为什么在框架里会大量使用设计模式呢?
很多人会说,因为框架需要覆盖的场景多,不仅要考虑现有功能的持续迭代,还要考虑到后续可能的功能扩展。代码结构既要足够稳定,保持可维护性。又要有一定的灵活性,保持可扩展性。
所以,框架通常需要一个比较好的设计。而设计最终体现在代码中,就是对设计模式的应用。
这说明设计模式所代表的经验,依然是有效的。
近10年来,软件生产效率有了巨大的提升。抛开硬件的因素,很大程度上是因为面向对象的普及,以及配套工具体系(例如框架)的完善。以至于在日常开发中,很多人会认为框架解决了所有问题,业务不过是CRUD。
所以在讨论业务需不需要设计模式之前,我们有必要先讨论下框架是否解决了业务开发的难点?
软件工程学上,将软件开发过程中的难题,分为本质困难和非本质困难。
本质困难是,如何抽象出实体,准确地描述现实业务中复杂的概念结构。非本质困难是,如何通过技术落地实现这些概念结构。
框架(编程语言也是)所解决的是非本质困难,是技术落地实现的效率问题。对于业务自身的复杂性这类本质困难没有帮助。
所以,业务上需要一些方法去对抗自身复杂性,以实现软件的可复用性、可维护性和可扩展性。而设计模式正是一种被验证过的有效方法。
当然,并不是所有业务都需要设计模式。如果业务复杂度在预期的时间段内是可控,可接受的,那么过度的设计,反而会降低软件的可维护性。
但是,在SaaS领域,业务复杂度的增速一般是非常快的,这跟SaaS软件的特性不无关系。回想这几年经历的SaaS行业,SaaS软件具备的区别于其他领域软件的几个明显特性:
这些特性,无一不在向我们表明着,SaaS软件是非常注重可维护性,可扩展性,甚至更多。
业务上我们是非常需要设计模式的。
至此,我们已经探讨了一部分意识上的问题,下面我们探讨一些方法上的问题。如果追问工作中为什么没有应用设计模式的经验,归结其原因,分为这么两类:
其原因在于,对设计模式的学习方法以及认知上存在偏差。例如,不知道设计模式的核心关注点在哪。或者认为设计模式都是割裂存在的,仅适用在单一场景下的解决方案的集合。
设计模式的本质是经验的总结。我们可以对设计模式进行抽象,更精确地表达它:
那么你认为最应该关注设计模式的哪一部分?
是名称吗? 每种设计模式的名字或者问题场景,你都非常清楚,但你可以熟练应用吗?
是解决方案吗? 解决方案固然重要,但是如果你不清楚方案要解决的问题,结果只能是拔剑四顾心茫然。
如果将设计模式的关注点放在问题场景(名称)或者解决方案上,大脑就会驱动我们以场景为触发点,去匹配模式。
通常会出现以下问题:
应用的场景数不胜数,场景背后的问题却是殊途同归。单个场景不难识别,但是实际业务开发中,往往是复合场景,识别难度大。这也是工作中难以应用设计模式的主要原因。
是问题吗? 问题是模式存在的前提,从使用方的角度看,问题是模式的唯一使用标识。所以说问题才是设计模式的核心。
模式基于问题提出,问题依托于场景存在。以问题为出发点,去匹配模式。
一个场景中可能包含多个问题,以创建对象场景为例。我们使用 问题=>模式
的思路来分析下在这个过程中可能会遇到的问题:
[建造者模式]
[工厂模式]
[原型模式]
[单例、享元模式]
一连串问题下来,你会发现,设计模式是伴随着问题的解决而被引入的(这里随着问题的细化确认,标出了几个比较明显的模式作为示例),这远比从场景套用模式来的更清晰自然。
总结起来就是,以具体场景为切入点,以遇到的问题为核心,匹配并组合模式,最终形成解决方案。
面向对象编程、设计原则、设计模式之间关系,恰似利剑、心法、剑法。手中有剑,心法为纲,方能知剑招,悟剑意。
封装 / 继承 / 多态是面向对象的3大基本特征。我们从设计模式的角度,该怎么去理解呢?
1.封装 :本质目的是将类实现者与使用者分离,从类内部来看,只包含自己的属性,尽量不依赖其他类,只暴露必要的行为。我们经常提到的高内聚,低耦合是对它最佳的体现。耦合强度由高到低排序,泛化( is-a
) = 实现( like-a
) > 组合( part-a
) > 聚合( contains-a
) > 关联( has-a
) > 依赖( use-a
)。
2.继承 :本质目的是抽象,是类与类之间的联系,表示的是 is-a
的静态关系。继承同时也具备复用属性与行为的能力。
3.多态 :本质目的是复用,但是只能复用行为,表示的是 like-a
的动态关系,在运行时体现出不同。
设计模式是基于对面向对象特性的充分理解,以及对类与对象之间相互关系的应用体现。
设计实现一个系统时,我们一般先按功能划分好模块,以模块中核心类为起点,根据功能逐步向周边延展设计其它类。
设计模式在这个过程中可以帮助我们进行高质量的代码设计。但是模式是有限的,这些优秀的设计模式背后有没有什么通用的指导原则呢?
is-a
的逻辑关联,目的在描述结构,而不是复用。本文围绕设计模式在业务中难以落地实践的现象,总结了一些可能的原因。跟随问题探讨了设计模式的本质以及对设计模式存在的一些错误认知,给出了以应用设计模式为目的,需要以问题为核心的学习方式。最后讲解了设计模式背后的通用原则及共识,帮助我们更好的理解设计模式。
本文转载自公众号有赞coder(ID:youzan_coder)。
原文链接:
领取专属 10元无门槛券
私享最新 技术干货