23种设计模式不是所有都适合前端的,因为前端语言的限制只有部分设计模式在在前端使用软多。
23种设计模式
-
创建型 工厂模式(工厂方法模式、抽象工厂模式、建造者模式)、单例模式、原型模式
-
组合型 适配器、装饰器、代理、外观、桥接、组合、享元
-
行为型 策略、模板方法、观察者、迭代器、职责链、命令、备忘录、状态、访问者、中介者、解释器
前端常用设计模式
1、工厂模式
工厂模式:将new操作进行单独封装;遇到new时,就可以考虑用工厂模式。
(创建对象过程中)把对象的具体实现或接口与用户使用分开,用户只用调一下工厂函数的creact()就能生成该对象,不用使用new,不用处理相关的具体的细节。
UML类图
经典使用场景及代码演示
- jQuery中的
$('div') React.createElement- Vue异步组件
1、jQuery中的$('div')
最后三行就是经典的工厂模式。
2、React.createElement
react常规语法
底层实际是使用的React.createElement创建了一个虚拟DOM节点
底层源码示意。也是new了一个虚拟DOM节点的实例返回出来。这块就是工厂模式。
3、Vue异步组件
设计原则验证
构造函数和创建者分离。符合开放封闭原则。
2、单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点,这样的模式就叫做单例模式。
数据的共享,在程序的所有地方该对象都只有一个实例。通过一个函数来判断是否已创建该对象实例,是则反回实例,没有才创建对象实例。通过调用这个函数来创建这个对象的实例。
UML类图
传统UML类图(强类型语言)
如上UML图:前面是-的属性是private。前端是+的属性是public。
- 单例模式需要用到强类型语言面向对象中的private特性。
- ES6中没有private(typescript除外),可以使用闭包来实现类似功能。
代码演示
java代码使用单例模式(使用private实现)
如上图,只能在类的内部new实例,而且只能new一次。
如上图,不能使用new再去创建实例。
js中使用单例模式(Es6中class静态方法版本)
class SingleObject {
login() {
console.log('login...')
}
static getInstance() {
// 判断是否已经new过1个实例
if (!SingleObject.instance) {
// 若这个唯一的实例不存在,那么先创建它
SingleObject.instance = new SingleObject()
}
// 如果这个唯一的实例已经存在,则直接返回
return SingleObject.instance
}
}
const s1 = SingleObject.getInstance()
const s2 = SingleObject.getInstance()
s1 === s2 // true
js中使用单例模式(闭包版本)
class SingleObject {
login() {
console.log('login...')
}
}
// 定义一个静态方法,使用闭包每次调用返回的都是同一个实例
SingleObject.getInstance = (function () {
let instance
return function () {
if (!instance) {
instance = new SingleObject()
}
return instance
}
})()
// 注意:这里只能使用静态方法getInstance,不能new SingleObject()
let obj1 = SingleObject.getInstance()
obj1.login()
let obj2 = SingleObject.getInstance()
obj2.login()
console.log('obj1 === obj2', obj1 === obj2) // true
--------------特别说明--------------
// js中使用new初始化一个实例也是能使用的,但是这时的实例已经是一个新实例了。并不符合单例模式了。
let obj3 = new SingleObject()
obj3.login()
console.log('obj1 === obj3', obj1 === obj3) // false
- 在一个静态方法中使用闭包来实现返回唯一的实例。
- js只能使用文档的形式来说明,使用错误也不会报错,无法完全实现单例模式。
场景
- jQuery,虽然不是严格的单例模式,但是也是单例模式的思想
- 模拟登录框
- 其它
- 购物车(和登录框类似)
- vuex和redux中的store
设计原则验证
- 符合单一职责原则,只实例化唯一的对象。
- 没法具体体现开放封闭原则,但是绝对不违反开放封闭原则。
3、适配器模式
旧接口格式和使用者不兼容,中间加一个适配器转换接口。
UML类图
传统UML类图
简化后的UML类图
使用场景
- 旧接口的封装
假如某个老项目中之前使用的都是jQuery中的ajax。而现在想要使用一个新ajax。如果批量赵替换风险很大。
这个时间就可以使用适配器模式,对新ajax进行一层封装,以适配之前的jQuery的写法。
- axios使用适配器同时兼容浏览器和node环境
axios在浏览器模式下使用的是ajax发送网络请求。在node环境下使用的是http模块发送网络请求。这也是使用的适配器模式在不同环境下进行适配。
设计原则验证
将旧接口和使用进行分离,符合开放封闭原则。
4、装饰器模式
为对象添加新功能,不改变其原有的结构和功能。
UML类图
传统UML类图
简化后的UML类图
使用场景(ES7装饰器)
1. 装饰类
装饰器原理
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A
装饰类demo
/*
* 一个见简单的demo
*/
function testDec(target) {
target.isDec = true
}
@testDec
class Demo {
// code...
}
alert(Demo.isDec) // true
/*
* 带参数的装饰器
*/
function testDec(isDec) {
return function(target) {
target.isDec = isDec
}
}
@testDec(true)
class Demo {
// code...
}
alert(Demo.isDec) // true
混入示例
function mixins(...list) {
return function(target) {
Object.assign(target.prototype, ...list)
}
}
const Foo = {
foo() {
alter('foo')
}
}
@mixins(Foo)
class MyClass {
}
let obj = new MyClass()
obj.foo()
2. 装饰方法
- 属性只读
- 给方法添加日志输出功能
3. 装饰器库core-decorators的使用
安装
npm i core-decorators --save
使用只读装饰器
废弃提示装饰器(适用于对外开放的类库,提高专业性)
设计原则验证
将现有对象和装饰器进行分离,两者独立存在。符合开放封闭原则。
5. 代理模式
使用者无权访问目标对象。中间加代理,通过代理做授权和控制。
UML类图
传统UML类图
简化后的UML类图
使用场景
1. js的事件代理
2.jQuery的proxy
常规用法,使用_this变量来传递this
$.proxy方式
3.ES6的Proxy(使用明星和经纪人的例子讲解)
设计原则验证
代理类和目标类分离,隔离开目标类和使用者。符合开放封闭原则。
代理模式与适配器模式、装饰器模式的对比
1. 代理模式 VS 适配器模式
适配器模式: 提供一个不同的接口(如不同版本的插头) 代理模式:提供一模一样的接口
2. 代理模式 VS 装饰器模式
装饰器模式: 扩展新功能,原有功能不变且可直接使用 代理模式:可使用原有功能,但是是经过限制或者阉割后的(第三者代理的)
6.外观模式
为子系统中的一组接口提供一个高层接口,使用者只能使用高层接口。
UML类图
使用场景
兼容多个功能的接口
另外,我认为后端的网关的功能和微前端框架也符合外观模式。
设计原则验证
不符合单一职责原则和开和封闭原则,因此谨慎使用,不可滥用。