javaScriptでのデザインパターンの使用:パターンの生成

こんにちは、Habr!

ハブに関する主題に関する詳細な記事がなかったことに驚き、すぐにこの重大な不正を修正するよう促しました。



Webアプリケーションのクライアント部分が厚くなっている状況では、ビジネスロジックが容赦なくクライアントに忍び寄っており、node.jsがサーバーテクノロジーの主権をますます大胆に侵害しています。javaScriptアーキテクチャの設計手法について考えるしかありません。 そして、この問題では、デザインパターンが間違いなく役立つはずです-頻繁に発生する問題を解決するためのテンプレートテクニック。 パターンは、必要に応じて変更を加えるのに最小限の労力で済むアーキテクチャの構築に役立ちます。 しかし、万能薬としてとらえないでください。つまり、大まかに言って、コードの品質が「噴水ではない」場合、ハードコードと論理的に独立したモジュール間の強固な接続でいっぱいになり、パターンは保存されません。 しかし、タスクがスケーラブルなアーキテクチャを設計することである場合は、パターンが役立ちます。

しかし、ちなみに、この記事はデザインパターンそのものではなく、javaScriptでのアプリケーションに関するものです。 この記事の最初の部分では、生成パターンの適用について説明します。





シングルトン



タスクがこのパターンを1つのフレーズで記述する場合、次のような結果になります。シングルトンは、インスタンスを1つだけ持つことができるクラスです。

このパターンを実装するjavaScriptの最も簡単で明白な解決策は、オブジェクトを使用することです。



var app = { property1: 'value', property2: 'value', ... method1: function () { ... }, ... }
      
      







この方法には長所と短所があります。 説明は簡単で、多くの人がパターンの存在を認識せずに使用します。この形式の記述は、javaScript開発者であれば誰でも理解できます。 ただし、重大な欠点もあります。シングルトンパターンの主な目的は、グローバル変数を使用せずにオブジェクトへのアクセスを提供することであり、このメソッドは現在のスコープでのみアプリ変数へのアクセスを提供します。 これは、グローバルな場合にのみ、アプリケーションのどこからでもアプリオブジェクトにアクセスできることを意味します。 ほとんどの場合、これは非常に受け入れがたいものです。javaScriptの優れた開発スタイルは、必要なすべてがカプセル化された最大1つのグローバル変数を使用することです。 これは、アプリケーションで上記のアプローチを最大1回使用できることを意味します。

2番目の方法はもう少し複雑ですが、より普遍的です:



 function SomeFunction () { if (typeof (SomeFunction.instance) == 'object') { return SomeFunction.instance; } this.property1 = 'value'; this.property2 = 'value'; SomeFunction.instance = this; return this; } SomeFunction.prototype.method1 = function () { }
      
      







これで、モジュラーシステム(requirejsなど)を使用して、アプリケーション内の任意の場所でこのコンストラクター関数の説明を含むファイルを接続し、次のようにしてオブジェクトにアクセスできます。



 var someObj = new SomeFunction ();
      
      







しかし、このメソッドには欠点もあります。インスタンスは、コンストラクターの静的プロパティとして単純に保存されるため、誰でも上書きできます。 しかし、どのような状況でも、アプリケーションの任意のコーナーから目的のオブジェクトにアクセスできることを望みます。 これは、インスタンスを保存する変数をプライベートにする必要があることを意味し、このクロージャーを支援します。



 function SomeFunction () { var instance; SomeFunction = function () { return instance; } this.property1 = 'value'; this.property2 = 'value'; instance = this; }
      
      







これがすべての問題の解決策のように思えますが、新しい問題が古い問題に取って代わります。 つまり、インスタンスの作成後にプロトタイプコンストラクターにリストされているすべてのプロパティは使用できません。 実際、それらは新しく作成されたコンストラクタではなく、古いコンストラクタに書き込まれます。 しかし、この状況から抜け出すには価値のある方法があります。



 function SomeFunction () { var instance; SomeFunction = function () { return instance; } SomeFunction.prototype = this; instance = new SomeFunction (); instance.constructor = SomeFunction; instance.property1 = 'value'; instance.property2 = 'value'; return instance; }
      
      







孤者を記述するこの方法は、上記のすべての欠点を欠いており、普遍的な使用に非常に適していますが、クロージャを使用して孤者を記述する方法はrequirejsでは機能しませんが、それらを少し変更し、関数自体によって作成されたクロージャから定義で使用される関数に変数を削除すると、その後、問題は解決されます。



 define([], function () { var instance = null; function SomeFunction() { if (instance) { return instance; } this.property1 = 'value'; this.property2 = 'value'; instance = this; }; return SomeFunction; });
      
      







工場方式



ファクトリメソッドには、主に2つの目標があります。

1)明示的に特定のクラスを使用しないでください

2)一般的に使用されるオブジェクト初期化メソッドを組み合わせます

ファクトリメソッドの最も単純な実装は次の例です。



 function Foo () { //... } function Bar () { //... } function factory (type) { switch (type) { case 'foo': return new Foo(); case 'bar': return new Bar(); } }
      
      







したがって、オブジェクトの作成は次のようになります。



 foo = factory('foo'); bar = factory('bar');
      
      







よりエレガントなソリューションを使用できます。



 function PetFactory() { }; PetFactory.register = function(name, PetConstructor) { if (name instanceof Function) { PetConstructor = name; name = null; } if (!(PetConstructor instanceof Function)) { throw { name: 'Error', message: 'PetConstructor is not function' } } this[name || PetConstructor.name] = PetConstructor; }; PetFactory.create = function(petName) { var PetConstructor = this[petName]; if (!(PetConstructor instanceof Function)) { throw { name: 'Error', message: 'constructor "' + petName + '" undefined' } } return new PetConstructor(); };
      
      







この場合、ファクトリが生成できるクラスの数に制限はありません。この方法で好きなだけクラスを追加できます。



 PetFactory.register('dog', function() { this.say = function () { console.log('gav'); } });
      
      







まあまたはそのような:



 function Cat() { } Cat.prototype.say = function () { console.log('meow'); } PetFactory.register(Cat);
      
      







抽象工場



相互接続または相互依存オブジェクトのグループを作成するには、抽象ファクトリーを使用します。

同じ要素で構成されるポップアップがいくつかあると仮定しますが、これらの要素は異なって見え、ユーザーのアクションに対して異なる反応をします。 これらの各要素は、ファクトリメソッドによって作成されます。つまり、各タイプのポップアップウィンドウには、独自のオブジェクトファクトリが必要です。

たとえば、BluePopupFactoryファクトリについて説明します。このファクトリはPetFactoryとまったく同じ構造であるため、詳細を省略して使用しています。



 function BluePopup () { //   } BluePopup.prototype.attach = function (elemens) { //  ui-   } BluePopupFactory.register('popup', BluePopup); function BluePopupButton () { //      } BluePopupButton.prototype.setText = function (text) { //    } BluePopupFactory.register('button', BluePopupButton); function BluePopupTitle () { //     } BluePopupTitle.prototype.setText = function (text) { //   } BluePopupFactory.register('title', BluePopupTitle);
      
      







おそらく、インターフェイス要素を担当する特定のクラスが必要です。



 function UI () { //,   ui- }
      
      







そして、createPopupメソッドを追加します:



 UI.createPopup = function (factory) { var popup = factory.create('popup'), buttonOk = factory.create('button'), buttonCancel = factory.create('button'), title = factory.create('title'); buttonOk.setText('OK'); buttonCancel.setText('Cancel'); title.setText('Untitled'); popup.attach([buttonOk, buttonCancel, title]); }
      
      







ご覧のとおり、createPopupはファクトリーを引数として受け取り、ポップアップ自体とタイトル付きのボタンを作成し、それらをウィンドウにアタッチします。

その後、このメソッドを次のように使用できます。



 var newPopup = UI.createPopup(BluePopupFactory);
      
      







したがって、無制限の数の工場を記述し、次のポップアップウィンドウを作成するときに必要な工場を転送することができます。



All Articles