11

几种常用设计模式的简单示例

 3 years ago
source link: https://segmentfault.com/a/1190000040554297
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

模式是在某一背景下某个问题的一种解决方案。

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

为了保证代码的可靠性、提高代码的复用率、使代码更容易被维护和阅读,我们需要了解并合理使用设计模式。

日常开发中,一些特定的场景下你的处理方法可能并不是很理想,往往这时借助一些设计模式可以让你优雅而高效的实现这些逻辑,下面就介绍一些虽然不是最全的但一定是最常用的设计模式。

单例模式:

定义:一个类只返回一个实例,一旦创建再次调用就直接返回。

使用场景:比如自定义弹窗,无论你程序中多少调用,都只应创建一个弹窗对象。

class CreateUser {
    constructor(name) {
        this.name = name;
        this.getName();
    }
    getName() {
        return this.name;
    }
};

const ProxyMode = (() => {
    let instance = null;
    return (name) => {
        if(!instance) {
            instance = new CreateUser(name);
        }
        return instance;
    }
})();

let a = ProxyMode('vn');
let b = ProxyMode('lb');

console.log(a, b);   // vn  vn    单例模式只会创建一次实例

策略模式:

定义:定义一个策略类只专注与各方法算法实现,定义一个接口调用这些方法。

特点:代码优雅,可读性高。

// 策略类
const levelObj = {
    "A": money => money * 4,
    "B": money => money * 3,
    "C": money => money * 2
}

// 环境类  封装调用接口
const getMoney = (level, money) => levelObj[level](money);

console.log(getMoney('A', 200))   // 800

代理模式:

定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。

使用场景:比如图片懒加载,先缓存动态 loading,必要时传入 src。

const imgFunc = (() => {
    let imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrc: (src) => {
            imgNode.src = src;
        }
    }
})();

const ProxyImg = (() => {
    let img = new Image();
    img.onload = () => {
        let node = document.getElementsByTagName('img')
        imgFunc.setSrc(img.src);
    }
    return {
        setSrc: (src) => {
            imgFunc.setSrc('../C3photo/jacky/1.jpg');
            img.src = src;
        }
    }
})();

ProxyImg.setSrc('../C3photo/jacky/2.jpg');

装饰者模式:

定义:装饰者模式能够在不改变对象自身的基础上,在运行程序期间给对象动态地添加职责。

使用场景:类似于拦截器,添加对象的前置和后置事件等。

Function.prototype.before = function(beforefn) {
    let _self = this;                          
    return function () {
        beforefn.apply(this, arguments);
        return _self.apply(this, arguments);
    }
}
Function.prototype.after = function(afterfn) {
    let _self = this;
    return function(){
        let ret = _self.apply(this, arguments);
        afterfn.apply(this, arguments);
        return ret;
    }
}
let func = function() {
    console.log('2');
}
//func1和func3为挂载函数
let func1 = function() {
    console.log('1');
}
let func3 = function() {
    console.log('3');
}

func = func.before(func1).after(func3);
func();   // 1  2  3

发布订阅模式:

定义:订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。

使用场景:微信公众号的订阅

let eventEmitter = {
    list: {}, 

    on(event, fn) {
        // 订阅
        let _this = this;
        _this.list[event] = _this.list[event] || [];
        _this.list[event].push(fn);
        return _this;
    },

    emit() {
        // 发布
        let _this = this;
        let event = [].shift.call(arguments),
            fns = _this.list[event];
        if (fns && fns.length) {
            fns.forEach((fn) => fn.apply(_this, arguments));
        }
        return _this;
    },

    off(event, fn) {
        // 取消订阅
        let _this = this;
        let fns = _this.list[event];
        if (!fns) return false;
        if (!fn) {
            fns.length = 0;
        } else {
            for (let i = 0; i < fns.length; i++) {
                if (fns[i] === fn || fns[i].fn === fn) {
                    fns.splice(i, 1);
                    break;
                }
            }
        }
    }
};

const user1 = (content) => {
    console.log("用户1订阅了:", content);
};

const user2 = (content) => {
    console.log("用户2订阅了:", content);
};

const user3 = (content) => {
    console.log("用户3订阅了:", content);
};

// 订阅
eventEmitter.on("article1", user1);
eventEmitter.on("article1", user2);
eventEmitter.on("article2", user3);

eventEmitter.emit("article1", "Javascript 发布-订阅模式");
eventEmitter.emit("article2", "Javascript 观察者模式");

eventEmitter.off("article1", user1);
eventEmitter.emit("article1", "Javascript 发布-订阅模式");

//用户1订阅了: Javascript 发布-订阅模式
//用户2订阅了: Javascript 发布-订阅模式
//用户3订阅了: Javascript 观察者模式
//用户2订阅了: Javascript 发布-订阅模式

学习设计模式不仅可以使我们用好这些成功的设计模式,更重要的是可以使我们深入理解面向对象的设计思想。

~本文完,感谢阅读!

学习有趣的知识,结识有趣的朋友,塑造有趣的灵魂!

大家好,我是〖编程三昧〗的作者 隐逸王,我的公众号是『编程三昧』,欢迎关注,希望大家多多指教!

你来,怀揣期望,我有墨香相迎! 你归,无论得失,唯以余韵相赠!

知识与技能并重,内力和外功兼修,理论和实践两手都要抓、两手都要硬!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK