肯定的。
在面向对象编程中,我们,开发者们,努力去把我们的世界模型化。如此,尝试和使用周围的世界作为一种描述我们的创造的工具是有意义的。
在这种情况下,我们从建筑(就是写建筑物和桥梁的那个)中选取一页,这本书有深远影响的建筑书籍叫【模式语言:乡镇,建筑,构造】,由 Christopher Alexander,Sara Ishikawa,Murray Silverstein 撰写,这里模式是这样描述的:
每个模式描述了一个在我们环境中不断发生的问题,同时描述了问题解决方案的核心,这种方式下你可以使用数千次这样的解决方法。不用同样的方式重复两次。
在软件开发中,建筑是用健康,健壮和可维护的方式构造的过程,并且模式提供了一种命名方法去解决常见问题。这些解决方案从抽象/形象到十分精确和专业中变化,并且让开发者有效的相互交流。
如果团队中两个或者更多开发者都有关于模式的认识,讨论问题的解决方案将会变得十分高效。如果只有一个开发者了解模式,那么跟团队的其他人员解释通常是简单的。
这篇文章的目的在于通过给你介绍软件设计模式的概念和展示一些有趣的在现在 JavaScript 项目中大量使用的模式来激发你对软件开发中一些正式表示的知识。
单例模式
是什么
单例模式并不是最广泛使用的,但是我们从这里开始是因为它比较好理解。
单例模式来源于单例的数学概念,如下:
在数学中,一个单例,也就是一个单元集,是一个确定元素的集合。比如:集合 {null} 是一个单例。
在软件中,简单解释为,我们为单个对象限制了类的实例。单例模式实现类的对象第一次应该被实例化,它实际上会得到实例。任何随后的实例化尝试将会返回第一个实例。
为什么
除了我们只能拥有一个超级英雄时(蝙蝠侠显然是),为什么我们会使用单例模式?
虽然单例模式本身没有问题(它之前被称作骗子,现在叫做恶魔),它仍然有自己的用处。最显著的是它可以作为配置对象的实例。你可能只需要在应用中有一个配置实例,除非提供多个配置是应用的特性。
哪里用
Angular 服务是单例模式在流行框架中使用的最好的例子。这篇文章在 Angular 文档中解释了如何确保服务始终是作为单例使用的。
单例服务有很多意义,因为服务用来储存状态,配置和允许组件交流,同时你想去确定没有多个实例搞乱这些概念。
作为例子,假设你有一个不重要的应用用来统计按钮的点击次数:
你应该保持按钮点击的数量的追踪,在一个对象里提供:
- 计数功能
- 提供当前点击数
如果对象不是单例模式(按钮有他们自己的实例),点击就不会正确。而且,哪一个计数实例是你提供组件展示当前计数的?
观察者模式
是什么
观察者模式定义如下:
观察者模式在软件设计模式中这样定义,一组叫做主题的对象,维护它的依赖列表,叫做观察者,同时自动通知它们任何状态改变,通常通过调用它们的方法。
观察者模式十分容易理解,如果我们跟真实世界的例子比较——报纸订阅。
通常可能发生的情况是,当你购买一份报纸,需要走到报刊亭,询问你喜欢的报纸的新期刊是否出版。如果没有,这是一个无效的糟糕情况,你不得不走回家过一会再来试试。在 JavaScript 术语中,情况类似,当不断轮询直到你得到自己想要的。
当你最后得到报纸,整个时间你可以做想做的——坐下来品尝咖啡看看报纸(同样,在 JavaScript 中,可以执行你一直想要的回调函数)。
聪明一点的做法(每天得到你最爱的报纸)是订阅这个报纸。
这种方式,当报纸的新期刊出版时,发行公司会通知你并且把报纸投递给你。不需要再跑到报刊亭。不需要在失望。狂欢吧。
在 JavaScript 中,你不再需要循环请求结果直到你运行函数。你可以,替代地,让主题知道你对一些事件(消息)感兴趣,然后提供一个回调,它会在新数据准备好后通知你。这时候,你就是观察者。
最棒的事情在于——你不一定是唯一的订阅者。当你因错过报纸而沮丧时,其他人也是。这就是为什么多个观察者可以订阅这个主题。
为什么
观察者模式有很多使用场景,但是通常,它应该被用于当你希望创建一对多的依赖,在对象没有紧密结合的对象间,同时可能让不定数量的对象知道何时状态改变。
JavaScript 对观察者模式来说是个好地方,因为一切都是事件驱动,而不是总在询问是否事件发生了,你应该让事件通知你(像是一句古老谚语:“不要电话我们,我们会电话你”)。
有可能你已经做了一些像观察者模式的事——addEventListener
。给元素添加事件监听具备所有观察者模式的特征:
- 你可以订阅一个对象
- 你可以取消订阅一个对象
- 对象可以对所有订阅者广播
学习观察者模式的最大优势在于你可以实行自己的主题或者更快抓住一个已经存在的解决方案。
哪里用
实现一个基本的观察者并不困难,不过这里有个很棒的库用于很多项目,它是 ReactiveX,它的 RxJS 是 JavaScript 版的。
RxJS 允许你不仅订阅主题,而且提供任何能想象的方式的转换数据的可能,组合多个订阅,处理异步工作更容易和很多很多其他的。如果你曾经想让你的数据处理和转换过程更上一级,RxJS是一个不错的学习的库。
除了观察者模式,ReactiveX 为实现了迭代器模式而自豪,它让主题可能知道它的订阅者何时订阅结束,从而有效地结束了主题订阅。这篇文章中我不解释什么是迭代器模式,它是个不错的练习,让你学习更多同时看看如何适应观察者模式。
外观模式
是什么
外观模式的名字取自建筑学。在建筑学中:
外观通常是建筑的外部一侧,一般是前部。这是来自法语,它的意思的“正面”或者“脸部”。
在建筑学中的外观是指建筑的外表,隐藏了内部的工作,软件开发的外观模式试着隐藏在正面之下深层的复杂,有效地让你通过 API 工作,这样当你想去改变隐藏代码时提供了改变的可能。
为什么
你可以在各种场景下使用外观模式,但是最显著的一个场景是让你的代码更容易理解(隐藏复杂)同时让依赖尽可能松耦合。
很容易看到为什么外观对象(或多对象层)是一件很棒的事。如果能避免,你不会想去面对恶龙。外观对象将给你提供不错的 API,同时处理所有恶龙自己的鬼把戏。
另外不错的事是我们在这里可以改变恶龙的外观,从没有接触其余应用的背景之中。假如你想用小猫来改变龙的外观。它仍然有爪子,但是更容易喂养。改变外观要重写一些代码,在外观下不需要改变任何依赖的对象。
哪里用
你经常看到外观的地方是 Angular 使用它的服务作为简单的背景逻辑。但不止是 Angular,下面的例子你也会看到。
如果说你想给自己的应用添加状态管理。你可以试试 Redux,NgRx,Akita,MobX,Apollo或者任何新流行的东西。那么,为什么不选择它们带它们兜兜风?
基本功能的状态管理库提供了什么给你?
可能有:
- 一种让状态管理指导你想去改变状态的方式
- 一种获得当前状态的方式
听起来不差。
现在,在你背带下用外观模式的力量,你可以为状态的每个部分修改外观,这些状态将会提供一个不错的 API 为你工作——比如像,facade.startSpinner()
, facade.stopSpinner()
,facade.getSpinnerState()
。这些方法容易理解和分析原因。
之后,你能处理外观,修改代码,让代码转换成在 Apollo 下有效的(用 GraphQL管理状态——如此正确)。你或许注意到它一点不适合你的代码方式,或者单元测试的方式不得不重新修改。不要紧,写一个新的外观,来支持 MobX。
从这里来,到哪里去
你可能注意到我们没有讨论过设计模式的代码实现。这是因为每一种设计模式在书中至少是一章内容。
现在我们来看看这些书,看看一两本深入的设计模式不会有什么坏处。
首先要推荐的是 设计模式:可复用的面向对象软件的基础,一堆大佬写的。这书有黄金屋的,软件设计模式的圣经。
如果你在找更简单的那么:head first 设计模式系列也不错。
但不止这些,试着搜索,读读不同的实现。即使你从来没有使用过模式或者技巧,你也能学到一些东西,在没有预料的方式下成长。