2014-09-29 3 views
0

Я прохожу через учебник Адди Османи по шаблону Decorator (найдено здесь http://addyosmani.com/blog/decorator-pattern/), и я немного запутался в том, как реализовать самый упрощенный Decorator в Javascript. Кажется, что некоторые примеры используют шаблон obj.prototype для добавления функциональности к существующему объекту, а некоторые создают автономную функцию и передают объект.Javascript Decorator Pattern - прототип или единственная функция?

// 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/


Мой вопрос (ы): Похоже, что я могу реализовать шаблон Decorator с помощью прототипичный наследования. Есть ли какие-либо плюсы или минусы для этого? (Т.е.: мой метод addCream())? Спасибо!

UPDATE:

Похоже, мой пример не на самом деле реализации шаблона Decorator на всех. И @Etai, и @Bergi дали отличные ответы ниже. Если я правильно понимаю, традиционный JS Decorator «обертывает» другой объект и затем корректирует поведение этого конкретного объекта без изменения базового объекта.

+1

Что вы спрашиваете * точно? * –

+0

@AlexWayne - Я обновил свой вопрос, извините, я был неясно. – Pathsofdesign

ответ

4

Нет. У вас там нет декораторов, они просто методы. Методы мутируют объект, Декораторы мутируют поведение методами перезаписи. Они очень похожи на mixins, только что они не создают новых методов.

Например, давайте ваш Coffee класса в setSize метод:

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

+1

@ Bergi, ваш пример прост и в действительности правильный. Мой пример показывает только теорию. +1 Вам, вероятно, следует принять ответ. – Etai

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'} 

Использование шаблона декоратора:

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; 
} 

Это позволяет что-то похожее на «множественного наследования», но изменить что-то в родительском позже не влияет на потомков, которые уже были созданы.

В общем, узор декоратора означает, что вы «украшаете» свой экземпляр, выполняя некоторый код, который изменит его.

EDIT: Обратите внимание, что, хотя я использовал этот пример, украшая свойства, как отметил @Bergi, декоратор действительно используется для украшения поведением (функциональностью). @ Ответ Берги на самом деле является классическим рисунком декоратора, который заключается в том, чтобы обернуть старый метод новым и, таким образом, «украсить» его. Мой пример - больше микса/удлинить рисунок. Однако основная идея шаблона заключается в том, что вы изменяете его во время выполнения и не наследуете от другого прототипа.

+0

Ах, ха! вы не знаете, насколько это мне помогло. Спасибо! – Pathsofdesign

+0

Я также нашел http://robdodson.me/blog/2012/08/27/javascript-design-patterns-decorator/, который соответствует тому, что вы сказали, и имеет некоторые простые плюсы и минусы. – Pathsofdesign