一,介绍
什么是设计模式?
书面定义:
设计模式(Design pattern)是编程中一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
个人理解:
如果把编程类比做习武的话,了解计算机原理,掌握数据结构与算法,吃透一门编程等可算作内功。内功深厚,便能对学武驾轻就熟。但光有内功,也止步于学武。真要coding,除内功外,也需招式。
而设计模式,就是前贤Gof帮我们从各种招式中,提炼总结出的一套绝世剑术。
前端与设计模式
目前前端的发展大方向是工程化,编程占比日渐提升。
设计模式,作为面向对象编程进阶的必备能力,值得想提升自己编程能力的前端同学深入学习。
给大家推荐下,本文用到的几份参考资料:
《Javascript设计模式与开发实践》书籍
设计模式-小浣熊网
设计模式-C语言中文网
另外,前端同学学习设计模式时,需要先理解Javascript面向对象与强类型语言面向对象的区别,毕竟大部分设计模式的例子都是基于Java的,需要先理解Java中如何实现封装,继承,多态。Java为什么有类和接口,才能够看懂市面上大部分的设计模式文章。
二,面向对象六个基本原则
前言:理解六个基本原则,需要对强类型OOP中接口的概念有一定的了解)
接口简单解释:强类型语言中,为了使代码具备多态特性(能灵活地切换调用类),在声明类之上,可以先声明一些接口,接口中只包含参数及方法的定义,不包含参数及方法的具体实现(依赖关系从依赖具体类转为依赖抽象接口,从而依赖某个接口的代码,可以随意调用实现了该接口的具体类)。
(对接口不理解的,可以看下《Javascript设计模式与并发实践》中1.1节的动态类型语言和鸭子类型)
1.单一职责原则
Single Responsibility Principle SRP原则
定义:一个接口只负责一个职责。
说明:负责的职责越单一,接口变更的概率就越小,代码就越健壮
2.开闭原则
Open-Closed-Principle OCP原则
定义:实体应当对拓展开放,对修改关闭。 说明:对已有功能进行修改,就可能会引入新的bug。所以应该尽量以拓展的形式来代替修改。但实现开闭原则的前提,是实体本身设计上有细粒度的拆分,例如满足单一职责原则,更容易区分每个接口是关闭修改还是开放拓展。
3.里氏替换原则
Liskov Substitution Principle LSP原则
定义:所有引用基类的地方必须能使用其子类的对象。
说明:确保继承的基本特性
4. 接口隔离原则
Interface Segregation Principle ISP原则
定义:使用多个专门的接口,而不使用单一的总接口。
说明:如果一个含有多个方法接口类服务于两个个具体实现,这两个具体实现都只依赖于这个接口类中的部分方法,那么就应该将这个接口类拆分为两个接口类,确保每个具体实现都只依赖了最少数量的接口方法。
5. 依赖倒置原则
Dependence Inversion Principle DIP原则
定义:高层不依赖低层,抽象不依赖细节。
说明:尽量将具体对象间的依赖关系,上升为其抽象接口间的依赖关系。这样可以保障即使由一方变更了,只要它的抽象接口不变更,就不会影响到依赖它的对象。降低耦合
6. 迪米特法则
Law of Demter LoD
也称为最少知识原则:Least Knowledge Principle LKP
定义:一个对象应该对其他对象有最少的了解。
说明:如果一个对象实现某个功能需要依赖于七八个对象,那么他变更的可能性就会很高。应尽可能有一个依赖,负责处理了相关的其他依赖,而对象本身,只需要依赖于那唯一一个依赖即可。
三,23种设计模式总览
设计模式共23种,可以被分为三类:
- 创建型。提供创建对象的机制,提升复用性和灵活性。
- 结构型。介绍对象可以组装成哪些更大的结构
- 行为型。关注于对象间的高效沟通和职责委派
简单介绍:
| 类型 | 模式名称 | 功能 | 前端场景 |
|---|---|---|---|
| 创建型 | 工厂方法模式 | 父类实现一个工厂方法,子类重写工厂方法来创建具体对象 | 不常见 |
| 创建型 | 抽象工厂模式 | 实现一个抽象工厂类,每个具体工厂类选择一系列的子类去创建对象 | 不常见 |
| 创建型 | 建造者模式 | 实现一个建造者类,由建造者类决定产品的创建细节。 | 不常见 |
| 创建型 | 原型模式 | 在类中提供clone方法, 创建新对象时能直接基于原有对象 | js对象本身基于原型 |
| 创建型 | 单例模式 | 在全局范围,只创建一个对象实例 | 全局对象,弹窗 |
| 结构型 | 适配器模式 | 原类之上,定义一个适配器类。负责处理各调用场景的兼容问题,对外提供统一的接口 | bom事件兼容,axios源码 |
| 结构型 | 外观模式 | 原类之上,定义一个外观类。简化原类的调用逻辑,对外提供简单的接口 | 不常见 |
| 结构型 | 装饰器模式 | 装饰器类用于接收对象作为入参,给对象增加统一的新功能 | ES6装饰器语法,mobx |
| 结构型 | 代理模式 | 原类之上,定义一个代理类。由代理类负责处理调用逻辑 | ES6-Proxy语法,vue3响应式原理 |
| 结构型 | 桥接模式 | 将类中的抽象与实现分离,使两部分可以独立变化。 | 不常见 |
| 结构型 | 组合模式 | 将有共性的类以树形结构组合,父节点中递归调用子节点,使调用者既可以操作整体,也可以操作局部 | 组件树,虚拟dom树 |
| 结构型 | 享元模式 | 拆分出类中可共享的细粒度单元,多个类间共享这些单元,以降低内存消耗 | 不常见 |
| 行为型 | 命令模式 | 需要调起多个执行接口的场景,通过统一调起命令接口,由命令接口再映射多个执行接口 | 状态管理中的action |
| 行为型 | 中介模式 | 若同类对象间有多对多的通信,则创建中介类,由中介类与每个对象进行通信 | 状态管理中的store |
| 行为型 | 观察者模式 | 消息源和消费者都注册到观察者类,由观察者来监听消息源对象,调用消费者对象。 | vue2响应式原理,rxjs |
| 行为型 | 责任链模式 | 将一系列操作以链式结构分解,上一个调用者只需要耦合下一个调用者 | webpack中间件,promise链式调用 |
| 行为型 | 状态模式 | 将对象的状态与对象分离,使对象各状态可以灵活变化,不影响到原对象 | 不常见 |
| 行为型 | 策略模式 | 将多个场景的执行逻辑放在多个策略类中,解耦上下文环境和执行方法间的映射逻辑。 | 表单校验 |
| 行为型 | 模板方法模式 | 先确定类的主体框架,将具体步骤的实现延迟到子类中,由子类去重写需要变更的具体实现 | 组件生命周期 |
| 行为型 | 访问者模式 | 将数据操作类,与数据结构类分离,使得在不改变数据结构类的前提下,可以拓展数据操作方式 | 不常见 |
| 行为型 | 迭代器模式 | 将集合的访问逻辑与集合自身分离 | 不常见 |
| 行为型 | 备忘录模式 | 需要缓存历史状态的场景,将缓存状态的动作放在发起类内部,管理快照的类只接受发起类获取快照 | react-memo |
| 行为型 | 解释器模式 | 在当前开发语言之外,再定义一套语法,简化某些特定操作,以更少的代码来实现 | 不常见 |