「 设计模式学习 」 L00 开坑贴 & 设计模式概览

设计模式是我在2018年在成都工作的时候听 肉山老师 的直播JavaScript 设计模式 导言的时候就想学习的一个东西,但是一直拖拖拖,买了肉老师的 Chat 从 jQuery 里学习设计模式,但是看了一头雾水,也同样买了肉老师推荐的书《设计模式:可复用面向对象软件的基础》,但是在家里吃灰了半年。
一直想把整个设计模式都了解一下啊,不用吃透,吃个大概明了就可,但是每次都提不起开坑的念头,如果再写成学习笔记的话,学习周期会大大拉长。

但是这在当前项目中的不断迭代中,我遇到了一个问题,就是 项目中的类型和状态管理,所以就想着是否有一个合适的设计模式来解决问题,所以就被逼迫着捡起来了……

肉老师谈论如何学习设计模式的方法,和我的学习想法是类似的,不管是学习设计模式还是学习一个框架/库,先了解一个大概心里有一个底,然后在实际遇到问题的时候再去找文档来炒冷饭,我觉得这样会更清晰一些。

《设计模式:可复用面向对象软件的基础》这本书是 1995 出版的,可能会有一些问题,或者有一些新的设计模式出现,但都不是问题,咱先把这本书内的 23 个设计模式了解,之后再看情况去了解新的内容。

所以这篇文章应该会陆陆续续的更新一些新内容,好了,在这篇标号 00 的笔记中我就以前端开发者的视角按照设计模式名称+概括+简单示例,来大概描述一下每种设计模式,如果有遇到可用场景时再深入的去记录。

这本书内把设计模式分成 三种类型( 创建型、结构形、行为型) 和 二十三个模式

创建型


1. 工厂方法 Factory Method

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂模式使一个类的实例化延迟到其子类。

工厂模式在实际应用中有很多,比如前端er最了解的 jQuery 中的 $() 方法查看文档,传入一个类名或者ID或者其它的值,返回回来一个DOM实例。中间的一些过程就不需要关注了,直接使用 $() 方法就会必定返回一个DOM实例。
讲人话,就是传入一些值,返回一个符合预期的结果。

2. 抽象工厂 Abstract Factory

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂模式是一个比较复杂的创建型模式,如果用最简单的话来说,可以描述成基于一组工厂方法,传入数据之后返回一个复杂对象,中间的步骤不需要关注。
有点类似于Vue中的高阶组件中包含了很多子组件和一些其它元素最后展现出一个渲染结果。
用廖雪峰老师的例子来举例就很容易让人明白 我们希望为用户提供一个Markdown文本转换为HTML和Word的服务 - 抽象工厂,虽然示例是Java,但是不影响理解。

3. 生成器 Builder

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

生成器模式,有些地方也叫建造者模式,用简单的话来概括就是传入一个或者一组数据,最后拼接返回一个结果,用Vue来举例的话就有点像 Reander 函数。
乍一看可能和抽象工厂很像,对确实很像,他们都是基于工厂模式的扩展,主要的区别就是抽象工厂模式对于中间的数据没有要求,而生成器重点关注如何分步生成复杂对象。

争议 这边的具体区别我还没有彻底弄明白确实需要整理之后来区别一下。具体得等这篇文章最后了。

4. 原型模式 Prototype

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

这个在Javascript中并不需要多理解,因为Js中每个数据类型都是对象,都拥有一个 prototype 属性,当然你也可以自己用代码来实现自己得原型模式其实显得很奇怪。大概可以用构造函数的视角来理解,只不过构造函数生成的实例对象的属性都是独立的,所以引入了 prototype 属性,来做到数据共享(修改其中一个,影响到其它所有)。

5. 单例模式 Singleton

保证一个类仅有一个实例,并提供一个访问它的全局访问点

类似于 window 对象,全局确保只有一个我们可以使用的 window 实例。
具体业务使用场景中,Vuex 也是单例模式,全局只会有一个Store实例来供用户去读取和修改。

我在查阅资料的时候稍微看到了还有人提到 惰性单例模式,这个之后再去了解

结构形


1. 适配器 Adapter

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式的话,我们可以这样来理解,很多时候我们不确定客户端浏览器是否支持我们所使用的API,这个时候就会写兼容,大概来写一个伪代码段来举例

1
2
3
4
5
6
7
var getByClass = function (className) { 
if(document.querySelector) {
return document.querySelector(className);
} else {
return document.getElementsByClassName(className)
}
};

就是那么简单,虽然有点强行解释,但这个就是适配器….

2. 桥接 Bridge

将抽象部分与它的实现部分分离,使它们都可以独立地变化

这个有点点像 UI 库中的 Notification 组件,在使用过程中我们并不关注于组件的具体展示,只需要调用 notification.open({config}) 函数传入参数就可以了。

3. 组合 Composite

将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

4. 装饰 Decorator

动态地给一个对象添加一些额外的职责。就增加功能来说,相比生成子类更为灵活。

看到一个很形象的比喻,现在UI库中的表单组件的表单验证,在不同的表单组件上使用不同的校验规则,而且并不影响组件本身,只是在提交之前额外调用了一下传入的校验函数。

5. 外观 Facade

为子系统中的一组接口提供一个一致的界面。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

6. 享元 Flyweight

运用共享技术有效地支持大量细粒度的对象。

7. 代理 Proxy

为其他对象提供一种代理以控制对这个对象的访问。

行为型


1. 职责链 Chain of Responsibility

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

2. 命令 Command

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

3. 解释器 Interpreter

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

4. 迭代器 Iterator

提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

5. 中介者 Mediator

用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

6. 备忘录 Memento

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

7. 观察者 Observer

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

8. 状态 State

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

9. 策略 Strategy

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

10. 模板方法 Template Method

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

11. 访问者 Visitor

表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

未完成


《设计模式:可复用面向对象软件的基础》
从 jQuery 里学习设计模式 - Meathill
设计模式 - 廖雪峰
设计模式 | 菜鸟教程
《深入设计模式》一本关于设计模式及其背后原则的电子书籍
Javascript继承机制的设计思想 - 阮一峰