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」(標準的なファクトリ)の記事をお読みください。

0 件のコメント: