幾種常用設計模式的簡單示例

程式設計三昧發表於2021-08-21

前言

模式是在某一背景下某個問題的一種解決方案。

設計模式(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 釋出-訂閱模式

總結

學習設計模式不僅可以使我們用好這些成功的設計模式,更重要的是可以使我們深入理解物件導向的設計思想。

~

~本文完,感謝閱讀!

~

學習有趣的知識,結識有趣的朋友,塑造有趣的靈魂!

大家好,我是〖程式設計三昧〗的作者 隱逸王,我的公眾號是『程式設計三昧』,歡迎關注,希望大家多多指教!

你來,懷揣期望,我有墨香相迎! 你歸,無論得失,唯以餘韻相贈!

知識與技能並重,內力和外功兼修,理論和實踐兩手都要抓、兩手都要硬!

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章