iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 24
0
Modern Web

JS Design Pattern 系列 第 24

JS Design Pattern Day24-開放-封閉原則 OCP(上)

第24天。最近要離開一個熟悉地方,覺得感傷。

今天來看看 開放-封閉原則

開放-封閉原則是被列在SOLID原則中,其定義是:軟體實體(類別、模組或是函數)應該是可以擴展的,但不可以修改。舉例子來說明,假設我們現在在執行大型專案,裡頭有非常多程式碼與檔案,如果今天有新需求要在window.onload中增加一些行為,比如說列印出所有畫面中元素總量,最簡單直接的做法當然就是直接編寫onload函數:

window.onload = function () {
    console.log(document.getElementsByTagName('*').length);
};

通常這樣子的需求產生時,會直接搜尋相關程式碼,直接改寫他們。但這樣修改程式碼的行為多少帶些風險,可能容易引起其他的問題。假設onload內容已經是數百行的巨大函數,在不改動程式碼的狀況下有什麼方法可以滿足呢?我們可以利用之前練習的裝飾者模式來處理這狀況:

Function.prototype.after = function (fn) {
    var _self = this;
    return function () {
        var ret = _self.apply(this, arguments);
        fn.apply(this, arguments);
        return ret;
    }
};

window.onload = (window.onload || function () { }).after(
    function () {
        console.log(document.getElementsByTagName('*').length);
    }
);

通過裝飾函數的方式,我們完全不用理會window.onload原本的行為。當需要改變或是新增一個程式功能的時候,可以使用增加程式的方式,但沒有改動到原本的程式碼,這樣就是開放-封閉原則。這樣的改法相較於直接改動程式碼更簡單也更安全。

在程式編寫中,過多的分支也是造成違反開放-封閉原則的一個常見原因。每當需求增加,就必須多一個else if,用switch意義上也是跟if else一樣的。實際舉個例子,現在我們要做一個各種動物叫聲的功能:

var makeSound = function (animal) {
    if (animal instanceof Duck) {
        console.log('呱呱');
    } else if (animal instanceof Dog) {
        console.log('汪汪');
    }
};

var Duck = function () { };
var Dog = function () { };

makeSound(new Duck());

這時候假如我們要增加一個狐狸的叫聲,我們勢必就要修改makeSound函數,增加一個else if分支來寫。現在我們可以利用物件多態的角度來修改:

var makeSound = function (animal) {
    animal.sound();
};
var Duck = function () { };
Duck.prototype.sound = function () {
    console.log('呱呱');
};
var Dog = function () { };
Dog.prototype.sound = function () {
    console.log('汪汪');
};
makeSound(new Duck());

這時候要增加狐狸叫聲就不用修改makeSound,直接加入物件:

var Fox = function () { };
Fox.prototype.sound = function () {
    console.log('Ring-ding-ding-ding-dingeringeding');
}

這樣增加程式碼,而不是修改程式碼的方式,符合開放-封閉原則。


上一篇
JS Design Pattern Day23-最少知識原則 LKP
下一篇
JS Design Pattern Day25-開放-封閉原則 OCP(下)
系列文
JS Design Pattern 30

尚未有邦友留言

立即登入留言