オブジェクト内でファクトリ クラスの代わりにファクトリ メソッドを使用することをお勧めするのはどのような場合ですか?
18 に答える
クラスが「人」であるという観点からデザイン パターンを考えるのが好きです。パターンとは、人々が互いに話し合う方法です。
ですから、私にとって工場のパターンは人材紹介会社のようなものです。可変数のワーカーを必要とする人がいます。この人は、雇った人について必要な情報を知っているかもしれませんが、それだけです。
そのため、新しい従業員が必要な場合は、人材紹介会社に電話して、必要なものを伝えます。さて、実際に誰かを雇うには、福利厚生や資格の確認など、多くのことを知る必要があります。
同じように、Factory を使用すると、コンシューマーは、作成方法や依存関係の詳細を知らなくても、新しいオブジェクトを作成できます。実際に必要な情報を提供するだけで済みます。
public interface IThingFactory
{
Thing GetThing(string theString);
}
public class ThingFactory : IThingFactory
{
public Thing GetThing(string theString)
{
return new Thing(theString, firstDependency, secondDependency);
}
}
そのため、ThingFactory のコンシューマーは、コンシューマーからの文字列データを除いて、モノの依存関係について知る必要なく、モノを取得できます。
ファクトリ メソッドは、コンストラクターの代わりと見なす必要があります。ほとんどの場合、コンストラクターの表現力が不十分な場合です。
class Foo{
public Foo(bool withBar);
}
次のように表現力がありません。
class Foo{
public static Foo withBar();
public static Foo withoutBar();
}
ファクトリ クラスは、オブジェクトを構築するための複雑なプロセスが必要な場合、構築に実際のクラスには必要のない依存関係が必要な場合、さまざまなオブジェクトを構築する必要がある場合などに役立ちます。
私が個人的に個別の Factory クラスが理にかなっていると思う状況の 1 つは、作成しようとしている最終的なオブジェクトが他のいくつかのオブジェクトに依存している場合です。たとえば、PHP の場合:House
オブジェクトがあり、そのオブジェクトに とオブジェクトがあり、オブジェクトの内部にもオブジェクトがあるとしますKitchen
。LivingRoom
LivingRoom
TV
これを実現する最も簡単な方法は、各オブジェクトにそれぞれのコンストラクト メソッドで子を作成させることですが、プロパティが比較的ネストされている場合、House
作成に失敗すると、失敗した原因を正確に特定するのにおそらく時間がかかります。
別の方法は、次のことを行うことです (派手な用語が好きな場合は、依存関係の挿入)。
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
ここで を作成するプロセスがHouse
失敗した場合、探す場所は 1 か所しかありませんが、新しいものが必要になるたびにこのチャンクを使用しなければならないのHouse
は便利とは言えません。工場に入ります:
class HouseFactory {
public function create() {
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
return $HouseObj;
}
}
$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();
ここのファクトリのおかげで、 を作成するプロセスHouse
は抽象化され (単に を作成したい場合にすべての依存関係を作成して設定する必要がないという点でHouse
)、同時に集中化されるため、保守が容易になります。別のファクトリを使用することが有益である理由は他にもありますが (テスト容易性など)、この特定のユース ケースは、ファクトリ クラスがどのように役立つかを最もよく示していると思います。
ファクトリまたはファクトリ メソッドを使用する背後にあるアイデアを明確に区別することが重要です。どちらも、相互に排他的な異なる種類のオブジェクト作成の問題に対処することを目的としています。
「ファクトリーメソッド」について具体的に説明しましょう。
まず、ライブラリや API を開発し、それをさらにアプリケーションの開発に使用する場合、ファクトリ メソッドは作成パターンの最適な選択肢の 1 つです。背後にある理由; 必要な機能のオブジェクトをいつ作成するかはわかっていますが、オブジェクトのタイプは未定のままであるか、渡される動的パラメーターによって決定されます。
ポイントは、ファクトリ パターン自体を使用してほぼ同じことを達成できることですが、上記の問題にファクトリ パターンを使用すると、システムに大きな欠点が 1 つ導入されます。異なるオブジェクト (サブクラス オブジェクト) を作成するロジックは、一部のビジネス条件に固有であるため、将来、ライブラリの機能を他のプラットフォーム用に拡張する必要がある場合 (より技術的には、基本インターフェイスまたは抽象クラスのサブクラスを追加して、ファクトリが既存のオブジェクトに加えてそれらのオブジェクトも返すようにする必要があります)いくつかの動的パラメーターに基づいて)、ファクトリ クラスのロジックを変更 (拡張) する必要があるたびに、コストのかかる操作になり、設計の観点からは良くありません。一方、「ファクトリーメソッド」なら
interface Deliverable
{
/*********/
}
abstract class DefaultProducer
{
public void taskToBeDone()
{
Deliverable deliverable = factoryMethodPattern();
}
protected abstract Deliverable factoryMethodPattern();
}
class SpecificDeliverable implements Deliverable
{
/***SPECIFIC TASK CAN BE WRITTEN HERE***/
}
class SpecificProducer extends DefaultProducer
{
protected Deliverable factoryMethodPattern()
{
return new SpecificDeliverable();
}
}
public class MasterApplicationProgram
{
public static void main(String arg[])
{
DefaultProducer defaultProducer = new SpecificProducer();
defaultProducer.taskToBeDone();
}
}
また、パラメーターの型が同じで動作が異なる複数の「コンストラクター」が必要な場合にも役立ちます。
次の場合は、オブジェクト内でファクトリ メソッドを使用することをお勧めします。
- オブジェクトのクラスは、作成する必要がある正確なサブクラスを認識していません
- オブジェクトのクラスは、それが作成するオブジェクトがサブクラスによって指定されるように設計されています
- オブジェクトのクラスはその義務を補助サブクラスに委譲し、正確にどのクラスがこれらの義務を負うかを知りません
次の場合は、抽象ファクトリクラスを使用することをお勧めします。
- オブジェクトは、その内部オブジェクトがどのように作成および設計されているかに依存するべきではありません
- リンクされたオブジェクトのグループは一緒に使用する必要があり、この制約を提供する必要があります
- オブジェクトは、親オブジェクトの一部となる、リンクされたオブジェクトのいくつかの可能なファミリの 1 つによって構成する必要があります
- インターフェイスのみを表示し、実装を表示しない子オブジェクトを共有する必要があります
それは本当に好みの問題です。ファクトリ クラスは必要に応じて抽象化/インターフェース化できますが、ファクトリ メソッドは軽量です (また、型が定義されていないため、テスト可能になる傾向がありますが、サービスに似た既知の登録ポイントが必要になります)。ロケータですが、ファクトリメソッドを見つけるためのものです)。
ファクトリ クラスは、返されるオブジェクト タイプにプライベート コンストラクタがある場合、異なるファクトリ クラスが返すオブジェクトに異なるプロパティを設定する場合、または特定のファクトリ タイプが返される具象タイプと結合されている場合に役立ちます。
WCFは ServiceHostFactory クラスを使用して、さまざまな状況で ServiceHost オブジェクトを取得します。標準の ServiceHostFactory は IIS によって.svcファイルのServiceHost インスタンスを取得するために使用されますが、WebScriptServiceHostFactory はシリアル化を JavaScript クライアントに返すサービスに使用されます。ADO.NET Data Services には独自の特別な DataServiceHostFactory があり、ASP.NET のサービスにはプライベート コンストラクターがあるため、ASP.NET には ApplicationServicesHostFactory があります。
ファクトリを使用するクラスが 1 つしかない場合は、そのクラス内でファクトリ メソッドを使用できます。
Order クラスと Customer クラスを設計する必要があるシナリオを考えてみましょう。シンプルさと初期要件のために、 Order クラスのファクトリの必要性を感じず、アプリケーションに多くの 'new Order()' ステートメントを入力します。物事はうまくいっています。
ここで、Customer 関連付け (新しい依存関係) なしでは Order オブジェクトをインスタンス化できないという新しい要件が明らかになりました。ここで、次の考慮事項があります。
1-新しい実装でのみ機能するコンストラクターのオーバーロードを作成します。(受け付けできません)。2- Order() シグネチャを変更し、すべての呼び出しを変更します。(良い習慣ではなく、本当の痛みです)。
代わりに、Order Class のファクトリを作成した場合は、1 行のコードを変更するだけで済みます。ほぼすべての集約関連付けに Factory クラスを使用することをお勧めします。それが役立つことを願っています。
ファクトリ クラスはより重量がありますが、特定の利点があります。複数の生データ ソースからオブジェクトを構築する必要がある場合は、構築ロジック (および場合によってはデータの集計) のみを 1 か所にカプセル化できます。そこでは、オブジェクト インターフェイスに関係なく、抽象的にテストできます。
これは有用なパターンであることがわかりました。特に、ORM を置き換えることができず不十分であり、DB テーブルの結合またはストアド プロシージャから多くのオブジェクトを効率的にインスタンス化したい場合に役立ちます。
私は工場を図書館の概念に例えます。たとえば、数値を操作するためのライブラリと、形状を操作するための別のライブラリを用意できます。Numbers
これらのライブラリの関数は、またはとして論理名が付けられたディレクトリに格納できますShapes
。これらは一般的な型で、形状の場合は整数、浮動小数点、倍長、長整数または長方形、円、三角形、五角形を含むことができます。
factory petter は、ポリモーフィズム、依存性注入、および制御の反転を使用します。
Factory パターンの目的は次のとおりです。Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
オペレーティング システムまたはフレームワークを構築していて、すべての個別のコンポーネントを構築しているとしましょう。
PHP の Factory パターンの概念の簡単な例を次に示します。私はそのすべてについて 100% ではないかもしれませんが、単純な例として役立つことを意図しています。私は専門家ではありません。
class NumbersFactory {
public static function makeNumber( $type, $number ) {
$numObject = null;
$number = null;
switch( $type ) {
case 'float':
$numObject = new Float( $number );
break;
case 'integer':
$numObject = new Integer( $number );
break;
case 'short':
$numObject = new Short( $number );
break;
case 'double':
$numObject = new Double( $number );
break;
case 'long':
$numObject = new Long( $number );
break;
default:
$numObject = new Integer( $number );
break;
}
return $numObject;
}
}
/* Numbers interface */
abstract class Number {
protected $number;
public function __construct( $number ) {
$this->number = $number;
}
abstract public function add();
abstract public function subtract();
abstract public function multiply();
abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Integer Implementation */
class Integer extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Short Implementation */
class Short extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Double Implementation */
class Double extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Long Implementation */
class Long extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
$number = NumbersFactory::makeNumber( 'float', 12.5 );
私の簡単な説明は、具体的なオブジェクトを作成するのに十分な情報がない場合にファクトリ パターンを使用するということです。依存関係がわからないか、オブジェクトの型がわかりません。これは実行時に得られる情報であるため、ほとんどの場合、私たちはそれらを知りません。
例: 乗り物オブジェクトを作成する必要があることはわかっていますが、それが飛ぶか地上で機能するかはわかりません。