# JS设计模式

JS设计模式 以及 原则

TIP

JS设计原则(SOLID)

  • S(single)单一职责原则:函数的功能单一

  • O(open-close)开放-封闭原则:对 扩展 开放,对 修改 封闭的(增加需求时,扩展新代码,而非修改已有代码)。

  • L(liskov)里氏替换原则:子类 可以代替父类。父类能出现的地方,子类就能出现。

  • I(interface)接口独立原则:接口的 功能单一

  • D(dependence)依赖倒置原则:只依赖抽象接口,不必关注具体类的实现

# 工厂模式

内部包装好一个对象,然后返回。(相当于工厂、车间)

function addPerson(name, age) {
    var obj = new Object();
    obj.name = name;
    obj.age = agge;
    obj.sayName = function() {
        console.log(this.name)
    }
}

const person1 = addPerson('heshiyu', 25);
const person2 = addPerson('zhouxingchi', 26)
1
2
3
4
5
6
7
8
9
10
11

应用场景:Reat.createElement、Vue.component异步组件等

# 单例模式

保证一个类仅有一个实例。

class Single {
    login() {}
}

Single.getInstance = (function() {
    let instance;
    return function() {
        if (!instance)  {
            instance = new Single()
        }
        return instance;
    }
})() // 利用闭包,将变量instance一直存在内存中(直到Single.getInstance为null)

const obj1 = Single.getInstance();
const obj2 = Single.getInstance();
console.log(obj1 === obj2); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

应用场景:Vuex中的Store、Redux中的Store、jQuery中的$

# 观察者模式

每个 被监听者 直接维护着它的 监听者列表。

  • ① 在Subject对象里维护一个“观察者列表”
  • ② 实例化Observer对象时,指定要监听的Subject对象
  • ② 当Subject对象的状态发生改变时,会通知每一个依赖它的Observers对象
class Subject {
    constructor() {
        this.state = 0;
        this.observers = [];
    }
    // 取值
    getState() {
        return this.state;
    }

    // 设置值
    setState(state) {
        this.state = state;
        this.notify(); // 状态发生改变,通知依赖
    }

    // 通知依赖
    notify() {
        this.observers.forEach(observer => observer.update());
    }

    // 添加依赖
    add(observer) {
        this.observer.push(observer);
    }
}

class Observer {
    constructor(name, subject) {
        this.name = name;
        this.subject = subject;
        this.subject.add(this)
    }

    update() {
        console.log(`${this.name}收到:得到更新${this.subject.getState()}`)
    }
}

let sub = new Subject(); // 实例化一个Subject对象
let observer1 = new Observer('观察者1号', sub); // 实例化观察者时,指定哪个Subject
let observer2 = new Observer('观察者2号', sub);

sub.setState(1); // Subject对象更新

// 输出:
// 观察者1号收到:得到更新1
// 观察者2号收到:得到更新1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

alt

Vue watch、NodeJS自定义事件

# 发布-订阅模式

和观察者模式非常相似,但是最大的区别在于:

在发布-订阅模式,发布者(publishers)不会直接将消息发送给特定的订阅者

var Event = (function(){
    var clientList = {}, // 订阅者列表
        listen, // 订阅
        trigger, // 触发通知
        remove; // 移除订阅

    listen = function(key, fn) {
        if(!clientList[key]){
            clientList[key] = []
        }
        clientList[key].push(fn)
    }
    trigger = function() {
        var key = Array.prototype.shift.call(arguments)
            fns = clientList[key]
        if(!fns || fns.length === 0) {
            return false
        }
        for(var i = 0, fn; fn = fns[i++];) {
            fn.apply(this, arguments)
        }
    }

    remove = function(key, fn) {
        var fns = clientList[key]
        if(!fns) {    
            return false
        }
        if(!fn){    
            fns && (fns.length = 0)
        } else {
            for(var l = fns.length - 1; l >= 0; l--) {
                var _fn = fns[l]
                if (_fn === fn) {
                    fns.splice(l, 1)
                }
            }
        }
    }

    return {
        listen,
        trigger,
        remove
    }
})()

Event.listen('squareMeter88', function (price) { // 订阅消息
    console.log('价格1:'+price)
})
Event.trigger('squareMeter88', 20000) // 价格1: 20000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

发布者和订阅者之间不知道对方的存在,需要通过消息代理来通信

特性 观察者模式 发布订阅模式
关系 直接联系 无直接关系,通过消息代理
耦合度 紧耦合 松耦合
适用情况 当组件之间依赖关系简单时 当组件之间依赖关系复杂时

alt

# 代理模式

通过代理,间接地访问目标对象。

// 源对象
class Jack {
    constructor (target) {
        this.target = target;
    }
    send (target, msg) {
        this.target.receive(msg)
    }
}

// 目标对象
class Rose {
    receive (msg) {
        console.log('收到消息: ' + msg)
    }
}

// 代理对象
class ProxyObj {
    constructor () {
        this.target = new Rose();
    }
    receive (msg) {
        this.send(msg)
    }
    send (msg) {
        this.target.receive(msg)
    }
}

const proxyObj = new ProxyObj();
const jack = new Jack(proxyObj);
jack.send(proxyObj, 'nihao'); // 收到消息:nihao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

ES6 Proxy、Vuex中对getters的访问

Jack:

  • this.target为Proxy
  • send

ProxyObj:

  • this.target为Rose
  • send
  • receive

Rose:

  • receive

# 中介者模式

现实中的中介者:博彩公司

  • 如果没有博彩公司,上千万的人一起计算赔率、输赢是非常困难
  • 有了博彩公司作为中介者对象,每个人只需跟博彩公司发生关联,由博彩公司来根据每个人的投注情况计算好赔率(彩民赢了,找博彩公司拿;输了就把钱交给博彩公司)

# 一个购买商品的例子:

alt

如图,如果没有使用任何设计模式,这里应该是在selectinput的各自onchange事件里,去获取当前用户所选的条件下的库存情况。

如果使用了中介者模式,只需增加一个中介者对象

var goods = {
    'red': 3
}

var mediator = (function() {
   var colorSelect = document.getElementById('colorSelect'),
       numberInput = document.getElementById('numberInput')
       nextBtn = document.getElementById('nextBtn')
   
   return {
       changed (obj) {
           var color = colorSelect.value,
               number = numberInput.value,
               stock = good[color]
               
               if (obj === colorSelect) { // 如果选择的是颜色下拉框
                   // ...
               } else if (obj === numberInput) { // 如果选择的是数量输入框
                   // ...
               }

               nextBtn.innerHTML = '购买'
       }
   }
})()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

可见,所有的对象会和中介者对象通信。当这些对象发生改变时,通知中介者对象,同时告诉中介者对象自己的身份,以便中介者辨别是谁发生了改变,剩下的事情就交给了中介者来完成。

好处:降低各个对象之间的耦合度

缺点:中介者对象自身往往难以维护

# 装饰者模式

装饰者模式可以 动态扩展一个实现类 的功能。

一幅画无论是否需要画框,都可以挂在墙上。但当用画框装饰时,可以让自己具有外壳保护、精美外观,同时又不被“伤害”。实际上最终是画框挂在墙上。

用处:

  • 扩展一个类的功能
  • 功能的动态增加、动态撤销
更新时间: 4/30/2020, 4:13:44 PM