ラベル design pattern の投稿を表示しています。 すべての投稿を表示
ラベル design pattern の投稿を表示しています。 すべての投稿を表示

2013年5月17日金曜日

design patterns – Factory


ファクトリ


ファクトリという名前は、オブジェクトの作成を単純化するという、その用途に由来しています。シンプルなファクトリは、新しいキーワードである用途すべてを抽象化したもので、クラス名が変更されたり他のクラス名で置き換えられたりしたとしても、1か所で変更を加えるだけで済みます。さらに、多数の異なる種類のオブジェクト、または異なるオプションを持つ1種類のオブジェクトを作成するための、ワンストップショップを設定します。標準的なファクトリについて手短に解説するのはちょっと難しいので、また後で説明します。

シンプルなJavaScriptファクトリ

この例は、サンプルのファクトリで、上記のデコレーターの例で生じた、「各種機能を搭載した自動車を作成するための実際のコードは長すぎる」という難題を使用しています。ファクトリを使用することで、この長いコードを減らして、1つの関数呼び出しにまとめることができます。最初に、1つの関数を含むオブジェクトリテラルを作成します。JavaScriptでは、オブジェクトリテラル/シングルトンは、シンプルなファクトリを構築する方法です。従来型のオブジェクト指向のプログラミング言語では、静的クラスがこれに相当します。

var CarFactory = { makeCar: function(features) { var car = new Car(); // If they specified some features then add them if (features && features.length) { var i = 0, l = features.length; // iterate over all the features and add them for (; i < l; i++) { var feature = features[i]; switch(feature) { case 'powerwindows': car = new PowerWindowsDecorator(car); break; case 'powerlocks': car = new PowerLocksDecorator(car); break; case 'ac': car = new ACDecorator(car); break; } } } return car; } }

このファクトリが持つ1つの関数がmakeCarであり、多数の煩雑な処理を実行します。まず第一に、関数が受け取る引数は、別のdecoratorクラスに対応付けられる文字列の配列です。makeCarはプレーンなCarオブジェクトを作成し、機能を反復処理し、自動車を装飾します。次に、これらの機能をすべて備えた自動車を作成しますが、最少でも4行のコードを記述する代わりに、次の1行だけが必要です。

Var myCar = CarFactory.makeCar(['powerwindows', 'powerlocks', 'ac']);

makeCar関数を調整して、任意の種類のデコレーターが1つだけ使用され、指定した順序で(実際に工場で製造されるように)アタッチされるようにすることができます。このコード例や、ファクトリパターンを効果的に使用する方法について詳しくは、筆者の個人ブログで「JavaScript Design Patterns: Factory」の記事をお読みください。

標準のファクトリ

標準のファクトリパターンは、シンプルなファクトリとはかなり異なりますが、もちろん、オブジェクト作成という役割を持つ点は同じです。シングルトンを使用するのではなく、クラスに対して単に抽象化メソッドを使用します。例えば、様々な自動車メーカーがあり、それぞれに専用の販売代理店があるとします。ほとんどの場合、すべての販売代理店は同じ販売方法を採用していますが、メーカーが製造する自動車だけが異なっています。したがって、すべての販売店は同じプロトタイプからメソッドを継承しますが、製造プロセスだけは独自のものを実装します。
上の説明を理解しやすいように、この例をコード化してみましょう。最初に、サブクラス化のみを目的としたmanufactureCarというメソッドを1つ持っている自動車販売代理店を作成します。これがスタブです。これは、サブクラスによって上書きされない限り、エラーを返します。

/* Abstract CarShop "class" */ var CarShop = function(){}; CarShop.prototype = { sellCar: function (type, features) { var car = this.manufactureCar(type, features); getMoney(); // make-believe function return car; }, decorateCar: function (car, features) { /* Decorate the car with features using the same technique laid out in the simple factory */ }, manufactureCar: function (type, features) { throw new Error("manufactureCar must be implemented by a subclass"); } };

sellCarmanufactureCarを呼び出すことに注目してください。これは、自動車を販売するには、manufactureCarがサブクラスによって実装される必要があることを意味しています。そこで、対になる自動車販売店を作成し、どのように実装するのか見てみましょう。

/* Subclass CarShop and create factory method */ var JoeCarShop = function() {}; JoeCarShop.prototype = new CarShop(); JoeCarShop.prototype.manufactureCar = function (type, features) { var car; // Create a different car depending on what type the user specified switch(type) { case 'sedan': car = new JoeSedanCar(); break; case 'hatchback': car = new JoeHatchbackCar(); break; case 'coupe': default: car = new JoeCoupeCar(); } // Decorate the car with the specified features return this.decorateCar(car, features); }; /* Another CarShop and with factory method */ var ZimCarShop = function() {}; ZimCarShop.prototype = new CarShop(); ZimCarShop.prototype.manufactureCar = function (type, features) { var car; // Create a different car depending on what type the user specified // These are all Zim brand switch(type) { case 'sedan': car = new ZimSedanCar(); break; case 'hatchback': car = new ZimHatchbackCar(); break; case 'coupe': default: car = new ZimCoupeCar(); } // Decorate the car with the specified features return this.decorateCar(car, features); };

メソッドの挙動は基本的に同じですが、それぞれが別種の自動車を製造するという点で異なります(ここでは必要ないため、carクラスの実装を省略して簡潔にしています)。ポイントは、manufactureCarメソッドがファクトリメソッドであるという点です。ファクトリメソッドは親クラスの抽象化であり、サブクラスによって実装され、オブジェクトの作成を担当します(各ファクトリメソッドは同じインターフェイスを備えたオブジェクトを作成します)。

仕上げ

ここでは、ファクトリパターンの基本について説明しました。ファクトリパターンについてもう少し詳しく知りたい場合は、筆者の個人ブログの「JavaScript Design Patterns: Factory」(シンプルなファクトリ)および「JavaScript Design Patterns: Factory Part 2」(標準的なファクトリ)の記事をお読みください。

design patterns – Decorator


デコレーター

デコレーターパターンは、他の多くのパターンとはかなり異なります。デコレーターパターンを使用すれば、機能の組み合わせごとにサブクラスを作成することなく、クラスへの機能の追加または変更という問題が解決されます。例えば、デフォルトの機能を備えたcarというクラスがあるとします。carには、車に追加できる多数のオプションの機能があります(自動ロック、パワーウィンドウ、エアコンなど)。サブクラスを使用してこれを処理しようとすると、すべての組み合わせに対応するには合計で8つのクラスが必要になります。
  • Car
  • CarWithPowerLocks
  • CarWithPowerWindows
  • CarWithAc
  • CarWithPowerLocksAndPowerWindows
  • CarWithPowerLocksAndAc
  • CarWithPowerWindowsAndAc
  • CarWithPowerWindowsAndPowerLocksAndAc
この方法は、オプションを1つ増やすだけで8つのサブクラスが追加されるので、収拾がつかなくなりやすいです。これはデコレーターパターンを使用することで解決できます。この方法では、新しいオプションを追加するたびに、1つのクラスを作成するだけで済み、クラスが倍増することはありません。

デコレーターの構造

デコレーターは、基本オブジェクト(Car)を、基本オブジェクトと同じインターフェイスを持つデコレーターオブジェクトでラップすることで機能します。デコレーターには、メソッドの処理方法に関して次のようなオプションがあります。
  1. ラップされているオブジェクトのメソッドを完全にオーバーライドすることができる
  2. デコレーターがメソッドの挙動に影響しない場合、ラップされたオブジェクトに単に渡される
  3. デコレーターは、ラップされたオブジェクトに呼び出しを渡す前または渡した後に挙動を追加できる
デコレーターは基本オブジェクトをラップできるだけでなく、他のデコレーターをラップすることもできます。これは、すべてのデコレーターが同じインターフェイスを実装しているからです。一般的な構造は、図2のような感じになります。
図2.デコレーターパターンの構造
図2.デコレーターパターンの構造

デコレーターパターンの例

上のcarの図をコード化してみましょう。まず、ベースのCarクラスを構築します。
var Car = function() { console.log('Assemble: build frame, add core parts'); }; // The decorators will also need to implement this interface Car.prototype = { start: function() { console.log('The engine starts with roar!'); }, drive: function() { console.log('Away we go!'); }, getPrice: function() { return 11000.00; } };
フレーズをコンソールに単に送信し、何が起こっているかを示すことで、構造をシンプルに保ちます。もちろん、自動車は複雑な機械なので、もっと多くの機能を追加することもできますが、このデモンストレーションではシンプルな構造のまま使用します。
次に、CarDecoratorクラスを作成します。これは抽象化クラスとなるのでクラス自体はインスタンス化しませんが、本格的なデコレーターを作成するためにサブクラス化する必要があります。
var CarDecorator = function(car) { this.car = car; }; // CarDecorator implements the same interface as Car CarDecorator.prototype = { start: function() { this.car.start(); }, drive: function() { this.car.drive(); }, getPrice: function() { return this.car.getPrice(); } };
いくつか重要なポイントがあります。まず、CarDecoratorコンストラクターは、carを取ります。さらに厳密に言うと、Carと同じインターフェイスを実装するオブジェクトを取ります。これには、複数のCarや、CarDecoratorサブクラスが含まれます。また、すべてのメソッドは、ラップされたオブジェクトに要求を渡すだけです。これは、すべてのデコレーターが、変更されていないメソッドに対して使用するデフォルトの挙動です。
次に、デコレーターを全部作成します。CarDecoratorから継承しているので、変更する機能をオーバーライドするだけです。他のすべての点で、CarDecoratorは完全に機能します。
var PowerLocksDecorator = function(car) { // Call Parent Constructor CarDecorator.call(this, car); console.log('Assemble: add power locks'); }; PowerLocksDecorator.prototype = new CarDecorator(); PowerLocksDecorator.prototype.drive = function() { // You can either do this this.car.drive(); // or you can call the parent's drive function: // CarDecorator.prototype.drive.call(this); console.log('The doors automatically lock'); }; PowerLocksDecorator.prototype.getPrice = function() { return this.car.getPrice() + 100; }; var PowerWindowsDecorator = function(car) { CarDecorator.call(this, car); console.log('Assemble: add power windows'); }; PowerWindowsDecorator.prototype = new CarDecorator(); PowerWindowsDecorator.prototype.getPrice = function() { return this.car.getPrice() + 200; }; var AcDecorator = function(car) { CarDecorator.call(this, car); console.log('Assemble: add A/C unit'); }; AcDecorator.prototype = new CarDecorator(); AcDecorator.prototype.start = function() { this.car.start(); console.log('The cool air starts blowing.'); }; AcDecorator.prototype.getPrice = function() { return this.car.getPrice() + 600; };
この例では、オリジナルの機能に追加されるメソッドを追加するたびに、親クラスのメソッドを呼び出すことはせず、単にthis.car.x()を呼び出します。通常は、親のメソッドを呼び出した方が適切なのですが、このコードは非常にシンプルなので、carのプロパティを直接呼び出した方が簡単だと判断しました。このようにすると、もし要求を渡す以外の機能を担わせるためにCarDecoratorに組み込まれたデフォルトの挙動を変更する必要が生じた場合に面倒なことになりますが、これは単なるサンプルなので、その局面は多分ないでしょう。
そういうわけで、必要な要素はすべて揃ったので、実際に作成してみましょう。
var car = new Car(); // log "Assemble: build frame, add core parts" // give the car some power windows car = new PowerWindowDecorator(car); // log "Assemble: add power windows" // now some power locks and A/C car = new PowerLocksDecorator(car); // log "Assemble: add power locks" car = new AcDecorator(car); // log "Assemble: add A/C unit" // let's start this bad boy up and take a drive! car.start(); // log 'The engine starts with roar!' and 'The cool air starts blowing.' car.drive(); // log 'Away we go!' and 'The doors automatically lock'
必要な機能を備えた自動車を作成するのは簡単です。自動車および必要なデコレーターを作成し、順次、最新の装備セットを備えた自動車をデコレーターに追加していきます。特に、CarWithPowerLocksAndPowerWindowsAndAcのような1つのオブジェクトを単にインスタンス化するのと較べると、作成コードは非常に長くなります。しかし心配は無用です。装飾されたオブジェクトを作成するよりスマートな方法を、ファクトリパターンに関する項でご紹介します。デコレーターパターンについてもう少し詳しく知りたい場合は、筆者の個人ブログの「JavaScript Design Patterns: Decorator」の記事をお読みください。

design patterns – Adapter


アダプター

アダプターパターンでは、ニーズに合うようにインターフェイスを変換(適応)させます。それには、必要なインターフェイスを備えたオブジェクトを別途作成し、そのオブジェクトを、インターフェイスを変更したいオブジェクトに接続します。
図1.アダプターパターンの構造
図1.アダプターパターンの構造

アダプターを必要とする理由

非常によくあることですが、アプリケーションの開発や保守を行っていて、アプリケーションのチャンク(例えばログやその種のデータの保管に使用しているライブラリ)を置き換える必要があると判断したとします。新しく置き換えるために用意したライブラリが、古いライブラリとまったく同じインターフェイスを持っている可能性は低いでしょう。この先の作業については、次の2つの選択肢があります。
  1. コード全体を確認し、古いライブラリを参照している部分をすべて変更する
  2. 新しいライブラリで古いライブラリとまったく同じインターフェイスを使用できるようにアダプターを作成する
アプリケーションが小さかったり、古いライブラリの参照が少ししかない場合は、新しい抽象化層でコードをコンパイルするよりも、コード全体を見直して新しいライブラリに合うようにコードを変更した方がよいと思えることもあるでしょう。とはいえ、多くの場合は、アダプターを作成した方が現実的で時間の節約になります。

アダプターの例

それでは、このコードサンプルに、上述の仮想のロガーシナリオを適用してみましょう。元の自分のコードで、ログの生成用にブラウザーに組み込まれているコンソールを使用しているかもしれませんが、それには若干問題があります。コンソールはすべてのブラウザーに組み込まれているわけではないので(特に古いブラウザーにはないことが多い)、自分のアプリケーションを他のユーザーが使用した場合に生成されるログを見ることができず、自己テストで検出できなかった問題を調べられないのです。そこで、ロガーを導入し、AJAXを使用してそれらのログをサーバーに転送して処理してもらうことにしました。新しいAjaxLoggerライブラリのAPIは以下のようになります。
AjaxLogger.sendLog(arguments); AjaxLogger.sendInfo(arguments); AjaxLogger.sendDebug(arguments); etc...
見たところ、このライブラリの作成者は、開発者がコンソールの置き換えにこのコードを使用することを想定していなかったため、各メソッド名の先頭に「send」を付ける必要があると考えたようです。そこで、「ライブラリを編集して、メソッド名を変更すれば済む話ではないか?」という疑問が出てきます。しかし、次の2つの理由により、その方法はお勧めしません。ライブラリをアップデートする必要が出てきたときに変更が上書きされてしまうので、開発者は再び名前変更の作業をやり直すことになります。また、ライブラリをコンテンツデリバリーネットワークからプルダウンする場合に名前を編集できないからです。そこで、新しいライブラリをコンソールと同じインターフェイスに適応させるオブジェクトを作成することにしましょう。
var AjaxLoggerAdapter = { log: function() { AjaxLogger.sendLog(arguments); }, info: function() { AjaxLogger.sendInfo(arguments); }, debug: function() { AjaxLogger.sendDebug(arguments); }, ... };

使い方

コンソールを使用する開発者はたいてい、参照によって直接コンソールを呼び出すものです。そこで、console.xxxの呼び出しごとに、コンソールではなく、新しいアダプターを参照するにはどうすればよいでしょう。ファクトリのような抽象化を使用していた場合、抽象化層に変更を加えるだけで済みますが、上で述べたとおり、皆が単にconsoleを直接参照しているのです。さて、JavaScriptは動的な言語であり、実行時に変更を行うことができます。そこで、新しいAjaxLoggerAdapterでコンソールをオーバーライドしてみたらどうでしょうか。
window.console = AjaxLoggerAdapter;
このやり方は簡単ですが、それだけに注意が必要です。他の人によって使用される前提のコードに対してこのようにすると、コンソールは、それらのユーザーの期待どおりには機能しなくなります。また、このコード例の単純さにだまされないでください。多くの場合、メソッドが簡単に対応し合う(sendLoglogのように)ことはありません。新しいライブラリと互換性を保つようにインターフェイスを変換するには、実際に自分のロジックを多少実装する必要があるかもしれません。

design patterns – Façade


ファサード

ファサードパターンは、この記事で紹介する最後のデザインパターンです。このパターンは関数やその他のコード片から成り、複雑なインターフェイスをシンプルにします。ファサードパターンは実際によく使用されており、大半の関数はこの目的で作られていると言えるでしょう。ファサードの目的は、大型のロジックを簡素化し、1つのシンプルな関数呼び出しにまとめることです。

ファサードの例

デザインパターンを使用しているとはまったく自覚せずに、実は既にファサードパターンをずっと使っているかもしれません。あらゆるプログラミング言語で使用されるライブラリはどれも皆、程度の差はあれ、ファサードパターンを使用しています。一般的に、このパターンは複雑なものをシンプルにできるからです。
例として、jQueryを見てみましょう。jQueryには、jquery()という、DOMの照会や要素の作成、DOM要素のjQueryオブジェクトへの変換など、多岐にわたる動作をする1つの関数があります。DOMの照会を調べて、この機能を作成するのに使用されたコードの行数にさっと目を通してみてください。きっと、「このコードを自分で書かないで済んでよかった」とほっとすることでしょう。それだけ長く複雑なコードなのです。ここでは

façadeパターンをうまく使って、数百行ものコードを短い関数1つに変換しています。

仕上げ

ファサードは非常にわかりやすいパターンですが、もし興味があれば、筆者の個人ブログの「JavaScript Design Patterns: Façade」の記事内により詳しい説明があります。

design patterns – Composite


コンポジット

シングルトンパターンの説明を読んで、「簡単だなぁ」と感じた方もどうかご心配なく。次は、もう少し複雑なパターンを2つ説明します。まず1つはコンポジットパターンです。コンポジットは、その名のとおり、1つのエンティテイを形成する複数のパーツで構成されるオブジェクトのことです。この1つのエンティテイは、すべてのパーツのアクセスポイントとして機能します。これを使うと、コードが非常にシンプルになりますが、コンポジットに含まれるパーツの数を把握する間接的な方法がないため、当てにならない面もあります。

コンポジットの構造

コンポジットの説明には図解が一番です。図1には、2つのタイプのオブジェクトがあります。コンテナとギャラリーはコンポジット、画像はリーフ(葉)です。コンポジットは子を持つことができますが、通常はあまり多くの挙動を実装しません。リーフには挙動の大半が含まれますが、少なくとも、従来のコンポジットの例では子を持つことはできません。
図1.コンポジットの構造
図1.コンポジットの構造
もう1つ、現実にあるコンポジットパターンで、皆さんがそうとは知らずに使っていたと思われる例をご紹介しましょう。コンピューターのファイル構造は、コンポジットパターンの一例です。例えば、フォルダーを削除すると、その中身もすべて削除されますが、これは本質的にコンポジットパターンの動作そのものです。ツリー構造の上部のコンポジットオブジェクトに対してメソッドを呼び出すことができ、メッセージは階層の下方へ伝達されます。

コンポジットコーディングの例

この例では、コンポジットパターンのサンプルとして画像ギャラリーを作成します。アルバム、ギャラリー、画像という3レベルの階層構造になります。図1の構成で言うと、アルバムとギャラリーはコンポジットで、画像はリーフに相当します。この構造は、コンポジットに必要とされるよりも明確な構造ですが、この例では、階層をコンポジットまたはリーフのみに制限した方が理にかなっています。標準的なコンポジットでは、リーフにできる階層レベルについて制限はありませんし、レベル数の制限もありません。
手始めに、アルバムとギャラリーの両方に使用されるGalleryCompositeという「クラス」を作成します。DOM操作にはjQueryを使用してコードを簡素にしていることに着目してください。
var GalleryComposite = function (heading, id) { this.children = []; this.element = $(' ') .append('

' + heading + '

'); } GalleryComposite.prototype = { add: function (child) { this.children.push(child); this.element.append(child.getElement()); }, remove: function (child) { for (var node, i = 0; node = this.getChild(i); i++) { if (node == child) { this.children.splice(i, 1); this.element.detach(child.getElement()); return true; } if (node.remove(child)) { return true; } } return false; }, getChild: function (i) { return this.children[i]; }, hide: function () { for (var node, i = 0; node = this.getChild(i); i++) { node.hide(); } this.element.hide(0); }, show: function () { for (var node, i = 0; node = this.getChild(i); i++) { node.show(); } this.element.show(0); }, getElement: function () { return this.element; } }
このコード例は長いので、ここで簡単に説明しておきましょう。addremovegetChildの各メソッドが、コンポジットの構築に使用されています。この例では、実際にはremoveおよびgetChildは使用されませんが、この2つのメソッドは動的なコンポジットの作成に役立ちます。hideshowgetElementの各メソッドは、DOMの操作に使用されます。このコンポジットは、ユーザーに表示されるページ上のギャラリーの表現として設計されています。コンポジットは、hideshowを介してギャラリー要素をコントロールできます。アルバムに対してhideを呼び出すと、アルバム全体が非表示になります。1枚の画像に対してのみ呼び出した場合は、その画像だけが非表示になります。
次に、GalleryImageクラスを作成します。このクラスでは、GalleryCompositeとまったく同じメソッドを使用することに着目してください。つまり、どちらのクラスも同じインターフェイスを実装します。ただし、画像はリーフであり、子を持つことはできないため、GalleryImageクラスは子に関するメソッドに対しては実際には何もしません。コンポジットを機能させるには、同じインターフェイスを使用する必要があります。コンポジット要素は、追加しようとする要素が別のコンポジット要素であるかリーフであるかを把握していないので、子に対してこれらのメソッドを呼び出そうとしたときにエラーにならないよう正常に機能させる必要があるからです。
var GalleryImage = function (src, id) { this.children = []; this.element = $('') .attr('id', id) .attr('src', src); } GalleryImage.prototype = { // Due to this being a leaf, it doesn't use these methods, // but must implement them to count as implementing the // Composite interface add: function () { }, remove: function () { }, getChild: function () { }, hide: function () { this.element.hide(0); }, show: function () { this.element.show(0); }, getElement: function () { return this.element; } }
以上で、オブジェクトのプロトタイプを作成できたので使ってみましょう。以下は、画像ギャラリーを実際にビルドするコードです。
var container = new GalleryComposite('', 'allgalleries'); var gallery1 = new GalleryComposite('Gallery 1', 'gallery1'); var gallery2 = new GalleryComposite('Gallery 2', 'gallery2'); var image1 = new GalleryImage('image1.jpg', 'img1'); var image2 = new GalleryImage('image2.jpg', 'img2'); var image3 = new GalleryImage('image3.jpg', 'img3'); var image4 = new GalleryImage('image4.jpg', 'img4'); gallery1.add(image1); gallery1.add(image2); gallery2.add(image3); gallery2.add(image4); container.add(gallery1); container.add(gallery2); // Make sure to add the top container to the body, // otherwise it'll never show up. container.getElement().appendTo('body'); container.show();
以上が、コンポジットに関する説明です。この画像ギャラリーの実際のデモは、筆者のブログのデモページでご覧いただけます。また、筆者のブログの「JavaScript Design Patterns: Composite」という記事では、このパターンに関してもう少し詳しく説明しています。

design patterns – Singleton


シングルトン

シングルトンパターンは、オブジェクトのインスタンスが必ず1つしか作られないようにしたい場合に使用します。従来のオブジェクト指向プログラミング言語では、静的なプロパティとメソッド、および動的なプロパティとメソッドの両方が存在するクラスが関係するため、シングルトン作成の概念は少々複雑であり、理解しづらいものでした。しかし、本稿で取り上げるのはJavaScriptであり、これは厳密なクラスを持たない動的な言語であるため、JavaScriptのシングルトンは非常にシンプルです。

シングルトンを必要とする理由

実装の詳細を解説する前に、シングルトンパターンをアプリケーションで使用すると便利な理由を説明します。オブジェクトのインスタンスを確実に1つに制限できるということは、非常に実用的です。サーバー側言語では、データベース接続の処理にシングルトンを使用することもできます。要求ごとにデータベース接続を繰り返し作成するのは、リソースの無駄でしかないからです。同様に、フロントエンドJavaScriptでは、すべてのAJAX要求を処理するオブジェクトをシングルトンにすることもできます。シングルトン化のルールは、まったく同じ機能を持つ新規インスタンスをいくつも作成するのならシングルトン化する、というシンプルなものです。
とはいえ、シングルトン化の理由はこれだけではありません。少なくともJavaScriptでは、シングルトンを使用することで名前空間のオブジェクトと関数を整理できるので、グローバル名前空間が無秩序になりません。グローバル名前空間が乱雑になるのは、サードパーティコードを使用している場合は特に、避けたい事態です。名前空間の定義にシングルトンを使用することを、モジュールデザインパターンとも言います。

シングルトンの例

シングルトンの作成にあたり実際に必要な作業は、オブジェクトリテラルの作成だけです。
var Singleton = { prop: 1, another_prop: 'value', method: function() {…}, another_method: function() {…} };
非公開のプロパティとメソッドを持つシングルトンを作成することもできますが、その場合はクロージャおよび自己実行型匿名関数を使用するため、作業が少し複雑になります。関数内では、いくつかのローカル関数と変数が宣言されます。次に、オブジェクトリテラルを作成して返します。このリテラルには、より大きな関数スコープ内で宣言した変数と関数を参照するメソッドをいくつか含みます。関数宣言の直後に()を挿入しておくと、外部関数が直ちに実行され、返されたオブジェクトリテラルが変数に割り当てられます。この説明がわかりづらいようでしたら、次のコードに目を通してください。説明は、コードの後に続きます。
var Singleton = (function() { var private_property = 0, private_method = function () { console.log('This is private'); } return { prop: 1, another_prop: 'value', method: function() { private_method(); return private_property; }, another_method: function() {…} } }());
関数内で先頭にvarを付けて宣言されている変数は、その関数内からのみアクセス可能です。さらに、アクセスできるのはその関数内で宣言されている関数群(例えば、オブジェクトリテラル内の関数群など)のみです。returnステートメントにより、外部関数の自己実行後にシングルトンに割り当てられるオブジェクトリテラルが返されます。

シングルトンを使用した名前空間の定義

JavaScriptでは、名前空間を定義するには、オブジェクトを別のオブジェクトのプロパティとして追加します。つまり、1層以上の階層構造になります。これはコードを論理セクションごとに分類する場合に便利です。YUI JavaScriptライブラリには名前空間に多数の階層が含まれ、少々過剰な感がありますが、一般には名前空間の入れ子構造は2~3階層以内に抑えることがベストプラクティスと考えられています。次のコードは、名前空間の定義の例です。
var Namespace = { Util: { util_method1: function() {…}, util_method2: function() {…} }, Ajax: { ajax_method: function() {…} }, some_method: function() {…} }; // Here's what it looks like when it's used Namespace.Util.util_method1(); Namespace.Ajax.ajax_method(); Namespace.some_method();
前述のとおり、名前空間の定義を使用することで、グローバル変数の数を最小限に抑えることができます。それどころか、もしその方がよければ、アプリケーション全体をappという名前のシングルトンオブジェクト名前空間にアタッチすることもできます。シングルトンデザインパターンおよび名前空間の定義での応用についてもっと知りたい場合は、筆者の個人ブログの「JavaScript Design Patterns: Singleton」に説明がありますので、ぜひご覧ください。

デザインパターン



Singleton Pattern シングルトン

Bridge Pattern

Composite Pattern コンポジット

Facade Pattern ファサード

Adapter Pattern アダプター

Decorator Pattern デコレーター

Factory Pattern 1 2 ファクトリ

Proxy Pattern プロキシ

Observer Pattern オブザーバー

Command Pattern コマンド

Chain of Responsibility Pattern















design patterns – command


コマンド

本稿およびこのシリーズの最後を飾るパターンはコマンドパターンです。従来のオブジェクト指向プログラミングのコンテキストの中では、このパターンは、標準から大きく外れています。通常は、オブジェクトは何らかの名詞を表すものですが(よって「オブジェクト」と呼ばれる)、このコマンドパターンでは、オブジェクトは動詞を表します。オブジェクトの役目は、メソッドの呼び出しをカプセル化することです。コマンドパターンとは、メソッドとそのメソッドを呼び出したいオブジェクトを実装するオブジェクト間の抽象層にすぎません。これは、オブジェクト指向言語が従来型であるほど便利であり、ユーザーインターフェイスに最もよく使用されます。JavaScriptでは、コマンドオブジェクトは単なる関数であることもあり、この関数はコードの単純化に大きく貢献します。

アラーム時計のコマンドパターン化

ここでは、従来のコマンドパターンの使い方をわかりやすく説明します。関係のないコードと混同せずにコマンドパターンの動作を示すために、この例では非常にシンプルなコードを使用します。この例はアラーム時計です。
var EnableAlarm = function(alarm) { this.alarm = alarm; } EnableAlarm.prototype.execute = function () { this.alarm.enable(); } var DisableAlarm = function(alarm) { this.alarm = alarm; } DisableAlarm.prototype.execute = function () { this.alarm.disable(); } var SetAlarm = function(alarm) { this.alarm = alarm; } SetAlarm.prototype.execute = function () { this.alarm.set(); }
このコードでは、少なくとも、enabledisablesetという3つのメソッドを持つアラームオブジェクトが既に作成済みであると仮定しています。これらの3つのメソッドをカプセル化するために3つの異なるクラスを作成しました。各クラスは、この場合はexecuteメソッドのみを持つ特定のインターフェイスを採用しています。
次のコードにはButtonクラスがあり、ボタンの文字列ラベルとコマンドオブジェクトという2つの引数を受け取ります。このコマンドオブジェクトのexecuteメソッドは、ボタンが押されたときに呼び出されます。本質的には、単に、UI要素へのバインディングイベントに対して従来のオブジェクト指向のアプローチを採用しているだけです。
var alarms = [/* array of alarms */], i = 0, len = alarms.length; for (; i < len; i++) { var enable_alarm = new EnableAlarm(alarms[i]), disable_alarm = new DisableAlarm(alarms[i]), set_alarm = new SetAlarm(alarms[i]); new Button('Enable', enable_alarm); new Button('Disable', disable_alarm); new Button('Set', set_alarm); }
これはアラームのリストで、各アラームは制御用のボタンを必要としています。そこで、各アラーム用に3つのコマンドオブジェクトを作成し、各アラームの3つのボタンにパラメーターとして送信します。この例では、JavaScriptの柔軟性により、すべてのコマンドオブジェクトを数行のコードに短くまとめることができます。
var makeCommand = function( object, methodName ) { return { execute: function() { object[methodName](); } } } // This is how it's used now: var alarms = [/* array of alarms */], i = 0, len = alarms.length; for (; i < len; i++) { var enable_alarm = makeCommand(alarms[i], 'enable'), disable_alarm = makeCommand(alarms[i], 'disable'), set_alarm = makeCommand(alarms[i], 'set'); new Button('enable', enable_alarm); new Button('disable', disable_alarm); new Button('set', set_alarm); }
特定のタイプのオブジェクトではなく、executeメソッドを備えたオブジェクトリテラルを返します。重要なのは、どちらの場合も、同じインターフェイスが使用されるということです。それでもまだ、成果の割に作業が多すぎるようです。シンプルなコールバック関数を使用して書き換えると、次のようになります。
var alarms = [/* array of alarms */], i = 0, len = alarms.length; for (; i < len; i++) { new Button('enable', alarm.enable); new Button('disable', alarm.disable); new Button('set', alarm.set); }
ただし、問題が1つあります。これらのコールバック関数を実行したとき、コンテキストが失われ、thisがアラームを参照しなくなり、大きな障害となります。そこで、コンテキスト変数を取るButtonコンストラクターを作成するか、アラームメソッドのプロキシを作成するか(ほとんどの場合コマンドパターンではこの方法を取る)、メソッド内でのアラームを参照するselfthatなどを使用できるようにクロージャを持つAlarmコードを書き直す必要があります。お気づきのように、既存のクラスに変更を加えずに済む唯一の方法は、プロキシまたはアラームとボタン間に別種の抽象層を使用するやり方だけです。コード行は増えますが、この方法は非常によい選択肢です。

問題の単純さ

この例における最も重要なポイントは、前にも述べましたが、これがコマンドパターンの仕組みを示すことを目的とした比較的シンプルな例であり、本来の性能や利便性を十分に表せてはいないということです。コマンドパターンは、もっとずっと便利に使用できます。上の例では、setメソッドは、アラームが設定する内容を把握できるよう、パラメーターをいくつか受け取ります。この場合、コマンドオブジェクトはパラメーターの特定に責任を負い、パラメーターをalarm.setに渡します。
他にコマンドパターンの使用が役立つ状況としては、alarmまたはその他のオブジェクトに対して複数のメソッドの呼び出しを行う場合などがあります。基本的に、execute関数が少しでも複雑になると、コマンドオブジェクトがより有用になります。
最後に、コマンドパターンが最も役に立つ状況は、executeundoなどの関数が2つ以上含まれている場合です。この場合は、execute関数が実行した任意のアクションを取り消すためのundo関数を作成できます。この関数を活用するには、ボタンがコマンドオブジェクトに対してexecuteメソッドを呼び出したときに、それ以前にexecuteメソッドを呼び出していた他のコマンドオブジェクトのスタックに、そのコマンドオブジェクトを積み重ねます。ユーザーがCtrl + Zキーを押して直前のアクションを取り消そうとした場合、スタックからコマンドオブジェクトが引き出され、そのundoメソッドが呼び出されます。このように使用すると、コマンドパターンは上の例よりもずっと便利でパワフルです。

コマンドの使用

コマンドパターンがこの記事の締めくくりです。コマンドパターンについてもう少し詳しく知りたい場合、またはJavaScriptにおけるコマンドオブジェクトの作成の別の方法について具体的に知りたい場合は、筆者のJavaScriptブログの「JavaScript Design Patterns: Command」の記事をお読みください。