2014-09-29 49 views
0

我正在運行Addy Osmani關於裝飾模式的教程(在這裏找到http://addyosmani.com/blog/decorator-pattern/),我對如何在Javascript中實現最簡單的裝飾器有點困惑。看起來有些示例使用obj.prototype模式爲現有對象添加功能,有些示例創建獨立功能並傳遞對象。Javascript裝飾模式 - 原型還是單一功能?

// Decorator Pattern ? 
function coffee(size, flavors) { 
    this._size = size || "medium"; 
    this._flavors = flavors || []; 
    this._cost = 100; 
    this.info = function() { 
     console.log(this._size, this._flavors, this._cost); 
    } 
} 

// Decorator - Would this be considered a decorator since the functionality needed to add flavors default to the coffee object? 

function addFlavor(coffee, flavor) { 
    coffee._flavors.push(flavor); 
    coffee._cost = coffee._cost + 25; 
} 

// Decorator - Engrave the cup? lol 
function engraving(coffee) { 
    coffee._cost = coffee._cost + 200; 
} 

// Decorator Variation w/ prototype instead - Add cream 
coffee.prototype.addCream = function() { 
    this._cost = this._cost + 100; 
}; 

// Instantiate Coffee 
testCoffee = new coffee('Large', ['vanilla']); 

// Add Flavors 
addFlavor(testCoffee, 'chocolate'); 
addFlavor(testCoffee, 'almond'); 
addFlavor(testCoffee, 'hazelnut'); 

// Add Engraving 
engraving(testCoffee); 

// Add Cream 
testCoffee.addCream(); 

// Log it all to the console 
testCoffee.info(); 

一本例中的jsfiddle可以在這裏找到:http://jsfiddle.net/pathsofdesign/ocbbzoy2/


我的問題(S):它看起來像我可以實現使用原型繼承Decorator模式。有沒有這樣做的優點或缺點(即:我的addCream()方法)? 謝謝!

UPDATE:

它看起來像我的例子不是實際執行Decorator模式可言。 @Etai和@Bergi都在下面給出了很好的答案。如果我理解正確,傳統的JS裝飾器會'包裝'另一個obj,然後調整該特定對象的行爲而不修改基礎對象。

+1

你究竟在問什麼* * – 2014-09-29 19:13:12

+0

@AlexWayne - 我更新了我的問題,抱歉我不清楚。 – Pathsofdesign 2014-09-29 19:22:34

回答

4

不,你有什麼沒有裝飾,他們只是方法。方法做變異的對象,修飾器通過覆蓋方法改變行爲。它們與mixins非常相似,只是它們不會創建新的方法。

例如,讓我們給你的CoffeesetSize方法:

Coffee.prototype.setSize = function(size) { 
    this._size = size || 'medium'; 
}; 

現在,讓我們有一個瘋狂的咖啡師誰沒有得到比例右:

function extreme(coffee) { 
    var oldMethod = coffee.setSize; 
    coffee.setSize = function(size) { 
     oldMethod.call(this, size && 'Xtra'+size[0].toUpperCase()+size.slice(1)); 
    }; 
} 

,並讓他擔任一個即訂購爲「大」:

> var coffee = extreme(new Coffee); 
> coffee.setSize("large") 
> coffee.info() 
XtraLarge, Array [], 100 
+0

+1爲例子/說明 – Pathsofdesign 2014-09-29 19:53:50

+1

@Bergi,你的例子很簡單,實際上是正確的。我的例子只是顯示了它背後的理論。 +1給你,這應該可能被接受的答案。 – Etai 2014-09-29 19:56:20

2

我覺得這個教程很混亂。

假設你想要創建一個simpleItem作爲你的基本項目,而一個complexItem是你的simpleItem加上更多。

使用原型繼承:

function SimpleItem(name){ 
    this.name = name; 
} 

function ComplexItem(size){ 
    this.size = size; 
} 
ComplexItem.prototype = new SimpleItem('complex'); 
var item = new ComplexItem(3); //{size: 3, name: 'complex'} 
ComplexItem.prototype.name = 'new complex name'; //item is now {size: 3, name: 'new complex name'} 

使用Decorator模式:

function SimpleItem(name){ 
    this.name = name; 
} 

function ComplexItem(size){ 
    SimpleItem.call(this, 'complex'); 
    this.size = size; 
} 

var item = new ComplexItem(3); //{size: 3, name: 'complex'} 
ComplexItem.prototype.name = 'new complex name'; //item is still {size: 3, name: 'complex'} 

雖然它看起來像ComplexItem從SimpleItem繼承,但事實並非如此。它由它裝飾。 這真的一樣這樣做:

function decorateMe(name){ 
    this.name = name; 
} 

function ComplexItem(size){ 
    decorateMe.call(this, 'complex'); 
    this.size = size; 
} 

這使得類似於「多重繼承」的東西,但後來在父母改變的東西不會影響到已經被創建的後裔。

一般來說,裝飾模式意味着你通過執行一些代碼來「裝飾」你的實例,這會改變它。

編輯:請注意,雖然我通過裝飾屬性使用此示例,正如@Bergi指出的那樣,裝飾器實際上用於裝飾行爲(功能)。 @ Bergi的答案實際上是古典的裝飾者模式,它是用一個新方法包裝舊方法,從而「裝飾」它。 我的例子更多的是混合/擴展模式。然而,模式的主要思想是你在運行時改變它,而不是實際上從另一個原型繼承。

+0

啊哈!你不知道這對我有多大幫助。謝謝! – Pathsofdesign 2014-09-29 19:49:52

+0

我還發現http://robdodson.me/blog/2012/08/27/javascript-design-patterns-decorator/它符合你所說的,並有一些簡單的優點和缺點。 – Pathsofdesign 2014-09-29 19:50:51