For background information on the decorator pattern check the Wikipedia article or for PHP implementations, here and here.
有关装饰器模式的背景信息,请查看Wikipedia文章或此处和此处的PHP实现。
动机和榜样使用 (Motivation and example use)
Let's take an example - you've created a class and released to the world. You want people to be able to easily build upon it and also release to the world. Some other folks can take your base class and selectively choose from the extensions only those that make sense for them. The example I choose is a class that does something on some text, beautifies it. Makes sure that there's always a space after the dots in a sentence and the dashes are also surrounded by spaces and so on. A Yahoo developer might want to add a feature (a decorator) that also adds an exclamation after the word Yahoo. A Spanish-speaking developer might add a feature where the exclamation sentences have the flipped exclamation mark before them. ¡Hola! Some people might add other language-specific or business-specific functionality. At the end, a new user of the class should be able to easily use the available features he likes, and leave out the rest. Let's see how the "client" code might look like:
让我们举个例子-您已经创建了一个类并发布给全世界。 您希望人们能够轻松地基于它并发布给世界。 其他一些人可以选择您的基础班,并仅从扩展中选择对他们有意义的扩展。 我选择的示例是在某些文本上执行某些操作的美化类。 确保句子中的点后始终有一个空格,并且短划线也被空格包围,依此类推。 Yahoo开发人员可能想要添加一个功能(装饰器),该功能在Yahoo一词之后也添加了感叹号。 会说西班牙语的开发人员可能会添加一个功能,使感叹号前面带有感叹号。 你好! 某些人可能会添加其他特定于语言或特定于业务的功能。 最后,该课程的新用户应该能够轻松使用他喜欢的可用功能,而忽略其余功能。 让我们看看“客户端”代码的样子:
// create an instance of the base class
// and initialize with some text
var t = new TextBeautifier.Core('Some text.And some more.Yeah, baby,yeah-Yahoo');
// get a decorator
t = t.getDecorator('Punctuation');
// get another one
t = t.getDecorator('FixDashes');
// another one
t = t.getDecorator('Yodel');
// call the method that will give
// a beautified result
t.get(); // "Some text. And some more. Yeah, baby, yeah - Yahoo!"
// change the input text
t.set('bla-bla-blah!Huh?');
// beautify again
t.get();
The idea is that no matter how many or how little decorators you add, the basic functionality (setting text input, getting beautified output) remains the same. Later you might want to add new decorators or remove some, but the method calls to get/set are still unchanged.
这个想法是,无论您添加多少个装饰器,基本功能(设置文本输入,获取美化输出)都保持不变。 稍后,您可能想要添加新的装饰器或删除一些装饰器,但是对get / set的方法调用仍然不变。
实作 (Implementation)
So let's see how this could be implemented in JavaScript. We have a base (core) class that provides functionality, in this simple case just setting and getting a text. In addition to that, the core class has a method getDecorator() that is used to add new decorating functionality.
因此,让我们看看如何用JavaScript来实现。 我们有一个提供功能的基类(核心),在这种简单情况下,只需设置和获取文本即可。 除此之外,核心类还具有用于添加新装饰功能的getDecorator()方法。
// just some namespeces
TextBeautifier = {};
TextBeautifier.Decorator = {};
// constructor of the base class
TextBeautifier.Core = function (text){
// store the text
TextBeautifier.Core.prototype.text = text;
// the basic get method
TextBeautifier.Core.prototype.get = function(){
return this.text; // might as well be TextBeautifier.Core.prototype.text
};
// the set [new text] method
TextBeautifier.Core.prototype.set = function(t){
TextBeautifier.Core.prototype.text = t;
}
// method that handles the decoration stuff
// this method accepts the name of the decorator
TextBeautifier.Core.prototype.getDecorator = function(deco){
// get the longer name of the decorator class constructor
var child = TextBeautifier.Decorator[deco];
// the decorator extends (inherits from)
// the parent class
child.prototype = this;
// return an instance of the new decorator
return new child;
}
}
The method getDecorator() is the most interesting since it contains the decoration logic. So what happens when we have an instance of the Core class, called t
and we say:
方法getDecorator()最有趣,因为它包含装饰逻辑。 因此,当我们有一个名为t
的Core类实例并说:
t = t.getDecorator('Punctuation');
The getDecorator does the following:
getDecorator执行以下操作:
- Figures out the name of the constructor of the new decorator, in this case it's TextBeautifier.Decorator.Punctuation 找出新装饰器的构造函数的名称,在本例中为TextBeautifier.Decorator.Punctuation
Makes the Punctuation class inherit the Core by setting the Puncuation prototype to point to
this
which is an instance of the Core class通过设置标点符号原型指向使得标点类继承核心
this
这是核心类的实例- Creates a Punctuation instance and returns it创建一个标点符号实例并返回它
So after the line:
所以在行之后:
t = t.getDecorator('Punctuation');
now t
is an instance of the Punctuation class which also has everything its parent Core class had.
现在t
是Punctuation类的实例,该类还具有其父Core类拥有的所有内容。
Next, we add another decorator;
接下来,我们添加另一个装饰器;
t = t.getDecorator('FixDashes');
Now t becomes an instance of the FixDashes class which inherits Punctuation, which in turn inherits Core. And so on. At first this might look like a normal inheritance chain but the difference is that the order of inheritance doesn't matter and can be changed at any time. The other beauty of this implementation of the decorator pattern is how simple the decorators are to implement. Inheritance is already taken care of by the Core class. A decorator simply implements a get() method that takes the output of the previous decorator and further beautifies is. Like:
现在t成为FixDashes类的实例,该类继承了Punctuation,后者又继承了Core。 等等。 乍一看,这看起来像是普通的继承链,但是区别在于继承的顺序无关紧要,可以随时更改。 装饰器模式实现的另一个好处是装饰器实现起来非常简单。 继承已由Core类处理。 装饰器仅实现get()方法,该方法获取先前装饰器的输出并进一步美化is。 喜欢:
// implementing a decorator
TextBeautifier.Decorator.Punctuation = function(){
// defining own get() method
this.get = function(){
// first get whatever was done so far by the previous
// class in the chain
var text = TextBeautifier.Decorator.Punctuation.prototype.get();
// then do your work and return
return text.replace(/([\.,!])\s*/g, '$1 ');
}
}
Now let's implement another decorator
现在让我们实现另一个装饰器
TextBeautifier.Decorator.FixDashes = function(){
this.get = function(){
var text = TextBeautifier.Decorator.FixDashes.prototype.get();
return text.replace(/\s*-\s*/g, ' - ');
}
}
Same thing - get what your parent does and further decorate the output. In this case the parent is Punctuation. Another decorator, same thing:
同样的事情-得到您父母的所作所为,并进一步装饰输出。 在这种情况下,父级是标点符号。 另一个装饰器,同样的事情:
TextBeautifier.Decorator.Yodel = function(){
this.get = function(){
var text = TextBeautifier.Decorator.Yodel.prototype.get();
return text.replace(/Yahoo/g, 'Yahoo!');
}
}
狂欢 (C'est tout)
Simple and elegant. Add decorators at will and combine to taste. This example used only one "working" method - get() - but the pattern doesn't force you to extend only one method, you can have as many methods that do something and can be further extended by decorators.
简洁大方。 随意添加装饰器,并结合起来品尝。 此示例仅使用一种“工作”方法-get()-但该模式不会强制您仅扩展一种方法,您可以具有执行某项操作的尽可能多的方法,并且可以由装饰器进一步扩展。
In this implementation you can also get the original raw text at any time using TextBeautifier.Core.prototype.get(), even in cases where you set() it further in the chain, this is because all decorators defined their own get(), they didn't mess up with the original prototype's get().
在此实现中,您还可以随时使用TextBeautifier.Core.prototype.get()获取原始原始文本,即使在链中更进一步设置(set)的情况下,这也是因为所有装饰器都定义了自己的get() ,他们没有弄乱原始原型的get()。
一棵圣诞树 (A Christmas tree)
I couldn't help but add another illustration. Say you have a class ChristmasTree and decorators Angel, HugeStar, BlueBalls, RedBalls, GoldenBalls, SilverBalls, SilverDust, GoldenGarlands, LightBulbs... you get the point. Then you go:
我忍不住又添加了一个插图。 假设您有一班ChristmasTree和装饰器Angel,HugeStar,BlueBalls,RedBalls,GoldenBalls,SilverBalls,SilverDust,GoldenGarlands,LightBlbs ...您明白了。 然后你去:
var tree2008 = new ChristmasTree();
tree2008.getDecorator('Angel');
tree2008.getDecorator('RedBalls');
tree2008.getDecorator('SilverDust');
tree2008.getDecorator('BlueBalls');
tree2008.gimme();
or
要么
var tree_at_myparents = new ChristmasTree();
tree_at_myparents.getDecorator('HugeStar');
tree_at_myparents.getDecorator('GoldenBalls');
tree_at_myparents.getDecorator('GoldenGarlands');
tree_at_myparents.getDecorator('LightBulbs');
tree_at_myparents.gimme();
Tell your friends about this post on Facebook and Twitter
在Facebook和Twitter上告诉您的朋友有关此帖子的信息
翻译自: https://www.phpied.com/a-javascript-implementation-of-the-decorator-pattern/