在原有的事物上,一點一滴地加上一些裝飾,這樣的概念套用在程式設計上,其實就是裝飾模式。
以計算金額來看,最終的計算結果是一個價格。但是這個價格也許還要再經過一些加工,例如markup、稅額等等,最終呈現的金額可能會因為各種條件而有所不同,這就可以利用裝飾模式來處理
一樣是從書中範例開始模仿學習,書中的範例提供了兩種方式,都能夠達到目的,這邊練習選擇比較簡單易懂的方式。
範例取自JavaScript設計模式
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
| var decorators = require('./Decorators')
class Sale { constructor(price) { this.price = price > 0 ? price : 100 this.decorators_list = [] this.decorators = decorators }
decorate(decorator) { this.decorators_list.push(decorator) }
GetPrice() { let price = this.price let max = this.decorators_list.length let name
for (let i = 0; i < max; i++) { name = this.decorators_list[i] price = this.decorators[name].GetPrice(price) } return price }
}
module.exports = Sale
|
這邊的重點在於定義流程,介面。並不實作細節。也就是說,Sale這個類別(姑且讓我這樣稱呼吧),他本身提供了兩個方法,一個方法是用來記錄有多少個decorator要使用。將他放在陣列裡面等候GetPrice()呼叫使用;而GetPrice()就更簡單了,將每一個decorator抓出來,然後把一開始的price經過剛才所設定的每一個decorator處理,最終再return這個結果。再更簡單的說,GetPrice()她只是將我們剛才設定要用的decorator,拿出來用而已。(實際的decorator部分,則是透過ctor的時候,從另外一隻檔案把資料抓過來,然後等著GetPrice()用this.decorators[name]這個方式去呼叫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var decorators = {} decorators.fedtax = { GetPrice: function (price) { return price + price * 5 / 100 } }
decorators.quebec = { GetPrice: function (price) { return price + price * 7.5 / 100 } } decorators.money = { GetPrice: function (price) { return price.toFixed(2) } } module.exports = decorators
|
裝飾模式的各種實作細節,都被放在這個物件裡面,如果是較為複雜的實作細節,那就可以再考慮針對這個部分來進行重構設計,不過目前為止,維持這樣就可以了。
這裡的重點在於將各種裝飾方法的名稱與實作方法這對key-value,放在這個decorators的物件內,方便我們定義的Sale物件來呼叫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var mocha = require('mocha') var chai = require('chai') chai.should()
var SaleObj = require('../src/Sale') describe('Sale', () => { it('#GetPrice()', () => { let expected = '112.88' let sale = new SaleObj(100) sale.decorate('fedtax') sale.decorate('quebec') sale.decorate('money') let actual = sale.GetPrice() actual.should.be.equal(expected) }); });
|
最終的程式碼與書上的並沒有太多改變,從測試程式中可以看到,模擬Client端呼叫使用的時候是透過一行一行的sale.decorate()呼叫,選擇不一樣的裝飾方法。這樣的方式在未來調整程式碼時,抽換演算法或是讓其他人瀏覽這份程式碼,都可以盡快的理解程式碼的意圖。
Code在文章都貼出來了….不過還是一樣照慣例附上程式碼….