五大设计原则 (SOLID)
S 单一职责原则
一个程序只做好一件事
功能独立拆开,保持每个部分干净独立 (便于组合与复用)
O 开放封闭原则
对扩展开放,对修改封闭
增加需求时,扩展新代码,而非修改已有代码
L 李氏置灰原则
子类能够覆盖父类
父类能够出现的地方,子类一样能出现
JS中使用较少(弱类型 & 继承使用较少)
I 接口独立原则
保持接口的单一独立,避免出现“胖接口”
JS中没有接口(typescript)除外,使用较少
类似于单一职责原则,这里更关注接口
D 依赖倒置原则
面向接口编程,依赖于抽象而不依赖于具体
使用方只关注于接口而不关注具体实现
JS中使用较少(没有接口 & 弱类型)
23种设计模式
设计模式分为:创建型(工厂模式、单例模式、原型模式)、结构型(适配器模式|装饰器模式|代理模式|外观模式|桥接模式|组合模式|享元模式)、行为型(策略模式|模板方法模式|观察者模式|迭代器模式|职责链模式|命令模式|备忘录模式|状态模式|访问者模式|中介者模式|解释器模式)。
工厂模式
将new 操作进行封装
遇到new时,就该考虑是否使用工厂模式。如:React.createElement(),源码自行查看哈
demo代码:
class Product {
constructor (name) {
name = name
}
init () {
console.log('init====')
}
fun1() {
console.log('fun1====')
}
fun2() {
console.log('fun2====')
}
}
class Factory {
constructor () {}
create() {
return new Product()
}
}
const factory = new Factory()
const product = factory.create()
console.log('product====', product)
product.init()
product.fun1()
product.fun2()
设计原则验证:
构造函数和创建者分离
符合开放封闭原则
单例模式
系统中被唯一使用的
一个类只有一个实例
demo代码:
class SingleObject {
constructor () {}
showCart () {
console.log('显示购物车')
}
}
SingleObject.getInstence = (function() {
let instance
return function() {
if (!instance) {
instance = new SingleObject()
}
return instance
}
})()
const singleObject1 = SingleObject.getInstence()
const singleObject2 = SingleObject.getInstence()
singleObject1.showCart() // log 显示购物车
singleObject2.showCart() // log 显示购物车
console.log('singleObject1===singleObject2', singleObject1===singleObject2) // true
设计原则验证:
符合单一职责原则,只实例化一个对象
没法具体开放封闭原则,但是不违反开放封闭原则
适配器模式
旧接口对使用者不兼容
中间加个适配器转换接口
demo代码
// 原代码jquery的ajax请求,想要去除jquery库,这样就导致$.ajax 不能被使用者使用,需要适配兼容。
// 新ajax请求
ajax(someoptions)
// 原项目中的jquery的ajax请求,当去除jquery后,需要保证原来的ajax请求功能可用
$.ajax()
// 兼容适配:
const $ = {
ajax: function(options) {
return ajax(options) // 兼容支持
}
}
设计原则验证:
将旧接口与使用者分离
符合开放封闭原则
装饰器模式
为对象添加新功能
不改变其原有结构与功能
class Circle {
constructor () {
}
draw () {
console.log('这是一个圆形')
}
}
class Decorator {
constructor (circle) {
this.circle = circle
}
draw () {
console.log('--------------------')
this.circle.draw()
this.setRedBorder()
}
setRedBorder () {
console.log('我是添加的红色边框')
}
}
const circle = new Circle()
circle.draw()
const deCircle = new Decorator(circle)
deCircle.draw()
设计原则验证:
将现有对象与装饰器进行分离,两者独立存在
符合开放封闭原则
代理模式
使用者无法直接使用目标对象
通过中间加代理来授权和控制目标对象
例如:事件代理、
demo代码
class RealImg {
constructor (fileName) {
this.fileName = fileName
this.loadImgFromDisk ()
}
display () {
console.log('显示真实图片')
}
loadImgFromDisk () {
console.log('加载本地图片'+this.fileName)
}
}
class ProxyImg {
constructor (fileName) {
this.realImg = new RealImg(fileName)
}
display () {
console.log('我是通过代理来显示图片' + this.realImg.fileName)
}
}
const proxyImg = new ProxyImg('名称1')
proxyImg.display()
设计原则验证:
代理类和目标类分离,隔离开目标类和使用者
符合开放封闭原则
外观模式
外观模式是一个胖接口
为子系统一组接口提供一个高层接口
使用者使用这个高层接口
demo代码
function bindEvent(ele, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
// ....
}
// 即可以传四个参数、也可以传三个参数
bindEvent(eleDom, 'click', '#selector', fn)
bindEvent(eleDom, 'click', fn)
设计原则验证:
不符合单一职责原则、开放封闭原则
谨慎使用,不滥用
观察者模式
发布 & 订阅
一对N
demo代码:
class Subject {
constructor () {
this.state = null
this.observes = []
}
setState (state) {
this.state = state
this.notifyAllObservers()
}
getState () {
return this.state
}
attach (observer) {
this.observes.push(observer)
}
notifyAllObservers () {
this.observes.forEach(observer => {
observer.update()
})
}
}
class Observer {
constructor (name, subject) {
this.name = name
this.subject = subject
this.subject.attach(this) //初始化观察者把自己添加到主题
}
/**
* 数据更新操作
*/
update () {
console.log(`${this.name}update, state: ${this.subject.getState()}`)
}
}
// 实例
const s = new Subject()
const ob1 = new Observer('m1', s)
const ob2 = new Observer('m2', s)
const ob3 = new Observer('m3', s)
s.setState(1)
s.setState(2)
s.setState(3)
设计原则验证:
主题和观察者分离,不是主动触发而是被动监听,两者解耦
符合开放封闭原则
迭代器模式
顺序访问一个集合
使用者无需知道集合的内部结构,能遍历访问每个元素
例如: jQuery each, ES6 Iterator
demo代码:
// 迭代器
class Iterator {
constructor (container) {
this.list = container.list
this.index = 0
}
next () {
if (this.hasNext()) {
return this.list[this.index ++]
}
return null
}
hasNext () {
if (this.index >= this.list.length) {
return false
}
return true
}
}
// 使用者
class Container {
constructor (list) {
this.list = list
}
// 生成迭代器工厂
createIterator () {
return new Iterator(this) // 传递当前Container 类的实例
}
}
// demo 测试
const list1 = [1,2,3,4,5,6]
const container = new Container(list1)
const iterator = container.createIterator() // 获得遍历器
while (iterator.hasNext()) {
console.log(iterator.next())
}
简单封装
function each(data) {
// 生成遍历器
let iterator = data[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
}
let arr = [1,2,3,4,5,6,7,8]
each(arr)
打印结果
设计原则验证:
迭代器对象和目标对象分离
迭代器将使用者和目标对象隔离开
符合开放封闭原则
状态模式
一个对象有状态变化
每次状态变化都会触发一个逻辑
不能都是if... else 来控制
demo代码
class State {
constructor (color) {
this.color = color
}
handle (context) {
console.log(`turn to ${this.color} light`)
context.setState(this)
// ... 其它操作逻辑
}
}
// 主体
class Context {
constructor () {
this.state = null
}
getState () {
return this.state
}
setState (state) {
this.state = state
}
}
const context = new Context()
const state1 = new State('red')
const state2 = new State('blue')
const state3 = new State('blank')
state1.handle(context) // 切换为red
state2.handle(context) // 切换为blue
state3.handle(context) // 切换为blank
结果:
设计原则验证:
讲状态对象与主题对象分离,状态的变化逻辑单独处理
符合开放封闭原则
原型模式
因重新创建一个对象new 个对象,开销比较大活不合适
基于原型对象(自己本身)clone 出一个对象
例如:Object.create(obj)
demo 代码:
const prototype = {
getInfo: function() {
return `name: ${this.name}, age: ${this.age}`
},
sayInfo: function() {
console.log(this.getInfo())
}
}
const obj1 = Object.create(prototype)
obj1.name = 'meng'
obj1.age = 32
obj1.sayInfo() // name: meng, age: 32
桥接模式
抽象与实现分离
符合开放封闭原则
demo代码:
class Color {
constructor (name) {
this.name = name
}
}
class Shape {
constructor (name, color) {
this.name = name
this.color = color
}
draw () {
console.log(`颜色:${this.color.name}, 形状:${this.name}`)
}
}
const red = new Color('red')
const circle = new Shape('circle', red)
const yellow = new Color('yellow')
const triangle = new Shape('triangle', yellow)
circle.draw()
triangle.draw()
设计原则验证:
抽象和实现分离,解耦
符合开放封闭原则
组合模式
生成树形结构,表示“整体-部分”的关系
让整体和部分都具有一致的操作性
例如:vnode 属性结构
demo代码
const vnode = {
tag: 'div',
attr: {
id: "app",
className: "container"
},
children: [
{
tag: 'div',
attr: {
id: 'item1',
className: 'item'
},
children: ['text node']
},
{
tag: 'div',
attr: {
id: 'item1',
className: 'item'
},
children: ['text node']
},
{
tag: 'div',
attr: {
id: 'item1',
className: 'item'
},
children: ['text node']
}
]
}
设计原则验证:
将整体和单个节点操作抽象出来
符合开放封闭原则
享元模式
共享内存,共享数据,共享部分与其它业务隔离,当需要修改或添加共享部分只修改共享块不用每个具体逻辑去修改
符合开放封闭原则
策略模式
不同策略,分开处理
避免出现大量if... else 或 switch...case
demo 代码:
class User {
constructor (type) {
this.type = type
}
// 需要改造buy
buy () {
if (this.type === 'ordinary') {
console.log('ordinary buy')
} else if (this.type === 'member') {
console.log('member buy')
} else if (this.type === 'vip') {
console.log('vip buy')
}
}
}
const ordinaryUser = new User('ordinary')
const memberUser = new User('member')
const vipUser = new User('vip')
ordinaryUser.buy()
memberUser.buy()
vipUser.buy()
console.log('改造后=====================================')
// 改造后
class OrdinaryUser {
buy () {
console.log('ordinary buy')
}
}
class MemberUser {
buy () {
console.log('member buy')
}
}
class VipUser {
buy () {
console.log('vip buy')
}
}
const ordinary = new OrdinaryUser()
const member = new MemberUser()
const vip = new VipUser()
ordinary.buy()
member.buy()
vip.buy()
模板方法模式
有几步处理或逻辑执行处理,进行统一封装在一个地方触发
demo代码
class Action {
init () {
this.handle1()
this.handle2()
this.handle3()
}
handle1 () {
console.log('handle1')
}
handle2 () {
console.log('handle2')
}
handle3 () {
console.log('handle3')
}
}
const action = new Action()
action.init()
职责链模式
各职责,链式操作
例如: promise.then.then、jquery 链式调用、node 的pipe管道流
demo代码:
class Action {
constructor (name) {
this.name = name
this.nextAction = null
}
setNextAction (action) {
this.nextAction = action
}
handle () {
console.log(`${this.name} 操作`)
if (this.nextAction != null) {
this.nextAction.handle()
}
}
}
const action = new Action('组长')
const jlAction = new Action('经理')
const zjAction = new Action('总监')
action.setNextAction(jlAction)
jlAction.setNextAction(zjAction)
action.handle()
命令模式
发布者与执行者隔离
通过中间加入命令者对象,作为中转站
demo代码:
// 接受者
class Receiver {
// 执行
exec() {
console.log('执行')
}
}
// 命令者
class Command {
constructor (receiver) {
this.recerver = receiver
}
cmd () {
console.log('执行命令/号令')
this.recerver.exec()
}
}
// 发布者/触发者
class Invoker {
constructor (command) {
this.command = command
}
invoke () {
console.log('发布/触发')
this.command.cmd()
}
}
const receiver = new Receiver()
const command = new Command(receiver)
const invoker = new Invoker(command)
invoker.invoke()
设计原则验证:
执行对象与命令对象分开
符合开放封闭原则
备忘录模式
随时记录一个状态的变化
随时可以恢复之前的某个状态(如撤销功能)
demo代码:
// 状态备忘
class Momento {
constructor (content) {
this.content = content
}
getContent () {
return this.content
}
}
// 备忘列表
class CareTaker{
constructor () {
this.list = []
}
// 添加备忘对象
add (momento) {
this.list.push(momento)
}
// 获取备忘对象
get (index) {
return this.list[index]
}
}
// 编辑器/操作
class Editor {
constructor () {
this.content = null
}
setContent (content) {
this.content = content
}
getContent () {
return this.content
}
saveContentToMomento () {
return new Momento(this.content)
}
getContentFromMomento (momento) {
this.content = momento.getContent()
}
}
const editor = new Editor()
const careTaker = new CareTaker()
editor.setContent('aaa')
editor.setContent('bbb')
careTaker.add(editor.saveContentToMomento()) // 将当前内容备忘列表
editor.setContent('ccc')
careTaker.add(editor.saveContentToMomento()) // 将当前内容备忘列表
editor.getContentFromMomento(careTaker.get(0)) // 从备忘列表获取第0个
console.log(editor.getContent()) // 获得当前内容
editor.getContentFromMomento(careTaker.get(1)) // 从备忘录列表获取第1个
console.log(editor.getContent()) // 获得当前内容
设计原则验证:
状态对象与使用者分开,解耦
符合开放封闭原则
中介者模式
通过关联对象,通过中介者隔离
符合开放封闭原则
demo代码:
class A {
constructor () {
this.number = 10
}
setNumber (num, meditor) {
this.number = num
if (meditor) {
meditor.setBNumber()
}
}
}
class B {
constructor () {
this.number = 20
}
setNumber (num, meditor) {
this.number = num
if (meditor) {
meditor.setANumber()
}
}
}
class Meditor {
constructor (a, b) {
this.a = a
this.b = b
}
setANumber () {
let number = this.a.number
this.b.setNumber(number * 10)
}
setBNumber () {
let number = this.b.number
this.a.setNumber(number/10)
}
}
const a = new A()
const b = new B()
const meditor = new Meditor(a, b)
a.setNumber(10, meditor)
b.setNumber(20, meditor)
console.log(a.number, b.number)
访问者模式
将数据操作与数据结构进行分离
使用场景不多
解释器模式
描述语言语法如何定义,如何解释和编译
用于专业场景,如果babel工具解释编译类的