跳至主要內容

超级简单的设计模式,看不懂你来打我

萌萌哒草头将军大约 5 分钟前端JavaScript设计模式

未经允许禁止转载

今天介绍几个经常被提到的设计模式,通俗易懂,包教包会
源码点击这里open in new window

单例模式

单例模式的精髓就是不管一个构造函数被实例化多少次,全局只有一个实例

const Singleton = (function () {
    let instance;
    function init () {
        return new Object();
    }
    return function () {
        if (!instance) {
            instance = init();
        }
        return instance;
    }
})()

let mySingleton1 = new Singltron();
let mySingleton2 = new Singltron();

console.log(mySing1 === mySing2) // true

单例模式的本质就是共享同一个作用域链,很明显这是JavaScript闭包机制实现的

观察者模式

观察者模式主要是通过一个目标(Suject)维护一系列的观察者(Observer),当目标发生变化时,通过广播事件,将目标具体的变化通知所有的观察者

观察者模式主要由四个角色组成:目标(Suject)观察者(Observer)具体目标具体观察者

下面的例子中,当data对象的name或者age属性发生变化时,都会对应的观察者会接受到变化

class Observer {
    constructor (code) {
        this.code = code;
    }
    update () {
        console.log('我是:', this.code, '我知道我该更新了')
    }
}

class Suject {
    constructor () {
        this.observerList = [];
    }
    
    addObserver (observer) {
        this.observerList.push(observer)
    }
    
    notify () {
        this.observerList.map((observer) => observer.update())
    }
}

// 具体的观察者
const concreteObservver1 = new Observer('concreteObservver1');
const concreteObservver2 = new Observer('concreteObservver2');
const concreteObservver3 = new Observer('concreteObservver3');
const concreteObservver4 = new Observer('concreteObservver4');

// 具体的目标
const concreteSuject1 = new Suject();
const concreteSuject2 = new Suject();

// 具体的对应关系
concreteSuject1.addObserver(concreteObservver1);
concreteSuject1.addObserver(concreteObservver2);

// 具体的对应关系
concreteSuject2.addObserver(concreteObservver3);
concreteSuject2.addObserver(concreteObservver4);

const data = {name: '萌萌哒草头将军', age: 18}

// 当data的name属性变化,对应的观察者concreteObservver1、
// concreteObservver2就会被广播事件通知,从而更新
Object.defineProperty(data, 'name', {
    get: () => this.name,
    set: (newValue) => {
        concreteSuject1.notify();
        this.name = newValue;
    }
})

// 当data的age属性变化,对应的观察者concreteObservver3、
// concreteObservver4就会被广播事件通知,从而更新
Object.defineProperty(data, 'age', {
    get: () => this.age,
    set: (newValue) => {
        concreteSuject2.notify();
        this.age = newValue;
    }
})

data.name = 'mmdctjj'
// 我是: concreteObservver1 我知道我该更新了
// 我是: concreteObservver2 我知道我该更新了
data.age = 18
// 我是: concreteObservver3 我知道我该更新了
// 我是: concreteObservver4 我知道我该更新了

如果Observerupdate方法里是跟新对应的dom,那恭喜你,这和vue的基思路理是一样的了

发布/订阅模式

虽然观察者模式可以轻松实现发布订阅模式的功能,但是观察者模式使得concreteSujectconcreteObservver耦合在了一起,对于复杂的系统,解耦才能算得上是优秀的系统。

发布/订阅者模式(Public/Subscribe),可以很好的解决观察者模式耦合问题,那么它是怎么解耦的呢?

发布/订阅模式提供了主题/事件通道(Topic/event Channer),,订阅者通过Subscribe功能和topic绑定,当发布者发生变化时,将所有的变更通过event广播给所有订阅该topic的订阅者们。这样就将发布者和订阅者完全隔离开了

还是上面的例子,我们用发布/订阅模式实现

class PubSub {
    constructor() {
        this.topics = {}
        this.uuid = 0 // 每个订阅者的唯一标识,可以随时取消订阅
    }
    // 发布器
    publish(topic, value) {
        if (this.topics[topic]) {
            this.topics[topic].map(({ event }) => event(value))
        }
    }
    // 订阅器
    subscribe(topic, event) {
        const uuid = this.uuid++
        this.topics[topic] = this.topics[topic]
            ? [...this.topics[topic], { event, uuid }]
            : [{ event, uuid }]
        return uuid
    }
}

const MyPubSub = (function () {
    let instance;
    function init() {
        return new PubSub();
    }

    return function () {
        if (!instance) {
            instance = init();
        }
        return instance;
    }
})()

const myPubSub = new MyPubSub()

const data = { name: '萌萌哒草头将军', age: 18 }

myPubSub.subscribe('data.name', (value) => console.log(
    '我知道',
    'name发生变化了:',
    value
))
myPubSub.subscribe('data.name', (value) => console.log(
    '我也知道',
    'name发生变化了:',
    value
))
myPubSub.subscribe('data.age', (value) => console.log('我知道', 'age发生变化了:', value))

Object.defineProperty(data, 'name', {
    get: () => this.name,
    set: (newValue) => {
        myPubSub.publish('data.name', newValue)
        this.name = newValue;
    }
})

Object.defineProperty(data, 'age', {
    get: () => this.age,
    set: (newValue) => {
        myPubSub.publish('data.age', newValue)
        this.age = newValue;
    }
})

data.name = 'mmdctjj'
// 我知道 name发生变化了: mmdctjj
// 我也知道 name发生变化了: mmdctjj
data.age = 18
// 我知道 age发生变化了: 18

jauery的实现了标准的发布订阅模式

$.trigger('login', {userName: 'MMDCTJJ', password: '*******'})
$.on('login', (userInfo) => console.log(userInfo))

vue中,可以使用$emit方法和$on

<button @click="$emit('increaseBy', 1)"> Increase by 1 </button>
<MyButton @increase-by="(n) => count += n" />

中介者模式

中介者模式也和观察者类似,中介者模式由中介者订阅者组成
所有的订阅者们不能互相沟通,必须通过中介者同步信息。

const mediator = (function () {
    let topics = [], uuid = 0;

    function subscribe (topic, callback) {
        uuid ++
        topics[topic] = topics[topic]
            ? [...topics[topic], { callback, uuid }]
            : [{ callback, uuid }]
    }

    function publish (topic, value) {

        if (topics[topic]) {
            topics[topic].map(item => item.callback(value))
        }
    }
    return {
        install: function (obj) {
            obj.uuid = uuid
            obj.publish = publish
            obj.subscribe = subscribe
            return obj
        } 
    }
})()

const subscribe1 = mediator.install({})
const subscribe2 = mediator.install({})
const subscribe3 = mediator.install({})

subscribe1.subscribe('data.name', (value) => console.log('我是subscribe1', value))
subscribe2.subscribe('data.name', (value) => console.log('我是subscribe3', value))

const data = { name: '萌萌哒草头将军', age: 18 }

Object.defineProperty(data, 'name', {
    get: () => this.name,
    set: (newValue) => {
        subscribe3.publish('data.name', newValue)
        this.name = newValue;
    }
})

data.name = 'mmdctjj'
// 我是subscribe1 mmdctjj
// 我是subscribe3 mmdctjj

策略模式

策略模式可以在不同的时机,采用不同的策略解决开发中的问题

相信你经常遇到同事这样的代码

// bad
let arr = [1, 2, 3]
arr.map(a => {
    if (a === 1) {
        console.log('11')
    } else if (a === 2) {
        console.log('22')
    } else if (a === 3) {
        console.log('33')
    }
})

这种如果需要优化,可以使用策略模式

class Strategy {
    constructor() {
        this.strategy = {} // JavaScript的策略模式可以通过对象来巧妙实现
    }

    addStrategy(name, callback) {
        this.strategy[name] = callback
    }

    execute(name, args) {
        this.strategy[name]?.(args)
    }
}

// good
let arr = [1, 2, 3]

const strategy = new Strategy()

strategy.addStrategy(1, () => console.log(11))
strategy.addStrategy(2, () => console.log(22))
strategy.addStrategy(3, () => console.log(33))

arr.map(a => strategy.execute(a, 'your args'))

装饰器模式

装饰器模式(Decorater),主要通过扩展已有的类增加新功能,它不会修改底层代码,是一种对象子类继承的替代方案

class YourClass {
    constructor () {
        this.number = 0
    }
    count () {
        this.number ++
        return this.number
    }
}

class Decorater {
    constructor (other) {
        this.number = other.number + 66
    }
    count () {
        this.number = this.number + 100
        return this.number
    }
}

const yourClass = new YourClass()
const decorater = new Decorater(yourClass)

console.log(yourClass.count(), 'yourClass') // 1
console.log(decorater.count(), 'decorater') // 166
console.log(yourClass.count(), 'yourClass') // 2
console.log(decorater.count(), 'decorater') // 266

好了,今天先分享到这里了,欢迎指正