ホームページ  >  記事  >  ウェブフロントエンド  >  Javascriptデコレータパターンの特徴と使用例を詳しく解説

Javascriptデコレータパターンの特徴と使用例を詳しく解説

伊谢尔伦
伊谢尔伦オリジナル
2017-07-24 13:42:291326ブラウズ

デコレーターパターン: 元のクラスと継承を変更せずにオブジェクト関数を動的に拡張し、オブジェクトをラップすることで元のオブジェクトと同じインターフェイスを持つ新しいオブジェクトを実装します。

Decorator パターンの特徴:

1. 元のオブジェクトの元の構造を変更せずに関数を追加します。

2. 装飾されたオブジェクトと元のオブジェクトは同じインターフェイスを備えているため、お客様は装飾されたオブジェクトを元のオブジェクトと同じように使用できます。

3. 装飾オブジェクトには、元のオブジェクトへの参照が含まれています。つまり、装飾オブジェクトは、実際の元のオブジェクトのパッケージ化されたオブジェクトです。

JavaScript デコレーター パターンの詳細な説明:

デコレーター パターンでは、実行時に追加の関数をオブジェクトに動的に追加できます。静的クラスを扱う場合、これは課題となる可能性があります。 Javascript では、オブジェクトは変更可能であるため、オブジェクトに機能を追加するプロセス自体は問題になりません。

デコレーター パターンの便利な機能の 1 つは、意図した動作をカスタマイズおよび構成できるという性質です。いくつかの基本機能のみを備えた通常のオブジェクトから開始し、次に、利用可能な装飾リソースのプールから通常のオブジェクトを強化するために必要な機能を選択し、特に装飾の順序が重要な場合は、それらを順番に装飾することができます。

デコレーター パターンを実装する 1 つの方法は、各デコレーターをオブジェクトにし、そのオブジェクトにオーバーロードする必要があるメソッドを含めることです。各デコレータは実際には、前のデコレータによって強化されたオブジェクトを継承します。各デコレーター メソッドは、「継承されたオブジェクト」に対して同じメソッドを呼び出してその値を取得し、さらにいくつかの操作を実行し続けます。

例 1 から始めましょう:


//需要装饰的类(函数)
function Macbook() {
 this.cost = function () {
  return 1000;
 };
} 
//计算商品的包装费
function PackagingFee(macbook) {
 this.cost = function () {
  return macbook.cost() + 75;
 };
}
//计算商品的运费
function Freight(macbook) {
 this.cost = function () {
  return macbook.cost() + 300;
 };
} 
//计算商品的保险费用
function Insurance(macbook) {
 this.cost = function () {
  return macbook.cost() + 250;
 };
}
// 用法
var myMacbook = new Insurance(new Freight(new PackagingFee(new Macbook())));
console.log(myMacbook.cost());//1625

上記のコードを簡単に分析してみましょう。上記のコードでは、合計 4 つの関数 (変更が必要な関数が 1 つと、変更に使用される関数が 3 つ) が定義されています。 。

次に、新しい Insurance オブジェクトを指す変数 myMacbook を宣言します。 Insurance オブジェクトの仮パラメータは、新しい Freight オブジェクトを指します。 Freight オブジェクトの仮パラメータは、新しい PackagingFee オブジェクトを指します。 object は新しい Macbook オブジェクトを指します。

次に、myMacbook のコストメソッドを呼び出します。上記の分析から、myMacbook.cost() の値は (Freight オブジェクトのコスト メソッド + 250) に等しく、Freight オブジェクトのコスト メソッドは (PackagingFee オブジェクトのコスト メソッドに等しい) と結論付けることができます。 + 300)、PackagingFee オブジェクトのコスト メソッドは (Macbook オブジェクトのコスト メソッド +75) と等しくなります。

最終結果は次のようになります: myMacbook.cost() の値 = 250 + (300 + (75 + 1000)) = 1625。


// 用法
var myMacbook = new Insurance(new Freight(new PackagingFee(new Macbook())));
console.log(myMacbook.cost());//1625 
//上面的代码等价于下面拆分后的代码,或许拆分后代码你更能看出前后的逻辑性
var macbook = new Macbook();
var package = new PackagingFee(macbook);
var freight = new Freight(package);
var myMacbook = new Insurance(freight);
//当然,如果你不想声明这么多变量(macbook、package、freight),只用一个变量也是可以的
var macbook = new Macbook();
macbook = new PackagingFee(macbook);
macbook = new Freight(macbook);
var myMacbook = new Insurance(macbook);

例 2 をもう一度見てください:


function ConcreteClass() {
 this.performTask = function () {
  this.preTask();
  console.log('doing something');
  this.postTask();
 };
}
function AbstractDecorator(decorated) {
 this.performTask = function () {
  decorated.performTask();
 };
}
function ConcreteDecoratorClass(decorated) {
 this.base = AbstractDecorator;
 this.base(decorated);// add performTask method
 decorated.preTask = function () {
  console.log('pre-calling..');
 };
 decorated.postTask = function () {
  console.log('post-calling..');
 };
}
var concrete = new ConcreteClass();
var decorator1 = new ConcreteDecoratorClass(concrete);
decorator1.performTask();
//pre-calling..
//doing something
//post-calling..

例 2 は実際には例 1 と非常によく似ています。簡単に分析してみましょう。まず例 2 では 3 つの関数が定義され、次に 2 つの変数 concrete とdecorator1 が宣言され、最後にdecorator1 の PerformTask メソッドが呼び出されます。

一見すると、ConcreteDecoratorClassにはperformTaskメソッドが存在しないように見えます。まず、次の 2 行のコードを分析してみましょう:


var concrete = new ConcreteClass(); //声明一个变量concrete指向new出来的ConcreteClass对象
var decorator1 = new ConcreteDecoratorClass(concrete); //声明一个变量decorator1指向new出来的ConcreteDecoratorClass对象,并传入变量concrete作为形参

次に、ConcreteDecoratorClass 関数のコードを 1 行ずつ分析してみましょう:


this.base = AbstractDecorator; //定义一个当前对象(decorator1)的base属性,并指向函数AbstractDecorator
this.base(decorated); //调用base属性指向的函数,也就是调用AbstractDecorator函数,同时传入形参decorated,形参decorated指向new出来的ConcreteClass对象

そういえば、 ConcreteDecoratorClass 関数のメソッド、重要なのは「this」を見ることです。

ConcreteDecoratorClass 関数の This は、新しい ConcreteDecoratorClass オブジェクトを指します (つまり、decorator1 と同じオブジェクトを指します)

AbstractDecorator 関数の This の鍵は、どのオブジェクトがこの関数を呼び出すかを確認することです。どのオブジェクト (コード「 this.base = AbstractDecorator; this.base(decorated);」 から、新しい ConcreteDecoratorClass オブジェクトが AbstractDecorator 関数を呼び出していることがわかります)。そのため、AbstractDecorator 関数の this は、新しい ConcreteDecoratorClass オブジェクトを指します (また、 Decorator1 によって指されるのは同じオブジェクトです)。 要約すると、上記のコードでは、

ConcreteDecoratorClass 関数の this であっても、AbstractDecorator 関数の this であっても、それらはすべて新しい ConcreteDecoratorClass オブジェクトを指していることがわかります。

そのため、decorator1.performTask() を実行すると、匿名関数 (decorated.performTask();) 内のコードが実行され続けます。匿名関数内の装飾された仮パラメータは、新しい ConcreteClass オブジェクトを指します。オブジェクトのperformTaskメソッドを実行します。

最後に例 3 を見てみましょう:

var tree = {};
tree.decorate = function () {
 console.log('Make sure the tree won\'t fall');
}; 
tree.getDecorator = function (deco) {
 tree[deco].prototype = this;
 return new tree[deco];
}; 
tree.RedApples = function () {
 this.decorate = function () {
  this.RedApples.prototype.decorate(); // 第7步:先执行原型(这时候是Angel了)的decorate方法
  console.log('Add some red apples'); // 第8步 再输出 red
  // 将这2步作为RedApples的decorate方法
 }
};
tree.BlueApples = function () {
 this.decorate = function () {
  this.BlueApples.prototype.decorate(); // 第1步:先执行原型的decorate方法,也就是tree.decorate()
  console.log('Put on some blue apples'); // 第2步 再输出blue
  // 将这2步作为BlueApples的decorate方法
 }
}; 
tree.Angel = function () {
 this.decorate = function () {
  this.Angel.prototype.decorate(); // 第4步:先执行原型(这时候是BlueApples了)的decorate方法
  console.log('An angel on the top'); // 第5步 再输出angel
  // 将这2步作为Angel的decorate方法
 }
};
tree = tree.getDecorator('BlueApples'); // 第3步:将BlueApples对象赋给tree,这时候父原型里的getDecorator依然可用
tree = tree.getDecorator('Angel'); // 第6步:将Angel对象赋给tree,这时候父原型的父原型里的getDecorator依然可用
tree = tree.getDecorator('RedApples'); // 第9步:将RedApples对象赋给tree
tree.decorate(); // 第10步:执行RedApples对象的decorate方法
//Make sure the tree won't fall
//Add blue apples
//An angel on the top
//Put on some red apples

例 3 は非常に複雑に見えますが、実際、分析ロジックは前の 2 つの例と同じです。例 3.次のコードの分析に焦点を当てましょう:

//tree.getDecorator('BlueApples')返回new出来的tree.BlueApples的实例对象,并将该对象赋值给空的tree对象
tree = tree.getDecorator('BlueApples'); //new出来的tree.BlueApples的实例对象的原型指向 --> 空对象tree 
//tree.getDecorator('Angel')返回new出来的tree.Angel的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.BlueApples的实例对象)
tree = tree.getDecorator('Angel'); //new出来的tree.Angel的实例对象的原型指向 --> tree.BlueApples的实例对象
//tree.getDecorator('RedApples')返回new出来的tree.RedApples的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.Angel的实例对象)
tree = tree.getDecorator('RedApples'); //new出来的tree.RedApples的实例对象的原型指向 --> tree.Angel的实例对象
//调用tree.decorate(),这里的tree已经是new出来的tree.RedApples的实例对象了。
//tree.RedApples的实例对象的decorate属性方法里面的第一行代码是 “this.RedApples.prototype.decorate()”
//结合上面的分析可以得出以下的原型链结构:
//this.RedApples.prototype --> tree.Angel;
//tree.Angel.prototype --> tree.BlueApples;
//tree.BlueApples.prototype --> 空对象tree
tree.decorate();

これを分析した後、最終的な出力結果を知ることは難しくありません。

以上がJavascriptデコレータパターンの特徴と使用例を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。