BuilderデザインパターンとFactoryデザインパターンの違いは何ですか?
どちらがより有利で、なぜですか?
これらのパターンをテストして比較/対比したい場合、どのようにして調査結果をグラフとして表すのですか
BuilderデザインパターンとFactoryデザインパターンの違いは何ですか?
どちらがより有利で、なぜですか?
これらのパターンをテストして比較/対比したい場合、どのようにして調査結果をグラフとして表すのですか
回答:
デザインパターンでは、通常、すべてのケースで機能する「より有利な」ソリューションはありません。何を実装する必要があるかによります。
ウィキペディアから:
- Builderは、複雑なオブジェクトを段階的に構築することに重点を置いています。Abstract Factoryは、製品オブジェクトのファミリー(単純または複雑)を強調しています。Builderは最終ステップとして製品を返しますが、Abstract Factoryに関する限り、製品はすぐに返されます。
- 多くの場合、ビルダーはコンポジットを作成します。
- 多くの場合、設計はファクトリーメソッド(それほど複雑ではなく、よりカスタマイズ可能で、サブクラスが増殖する)を使用して開始され、デザイナーがより多くの柔軟性が必要な場所を発見すると、抽象ファクトリー、プロトタイプ、またはビルダー(より柔軟でより複雑)に進化します。
- 作成パターンは補完的である場合があります。ビルダーは、他のパターンの1つを使用して、ビルドされるコンポーネントを実装できます。Abstract Factory、Builder、およびPrototypeは、実装にシングルトンを使用できます。
ファクトリーデザインパターンのWikipediaエントリ:http : //en.wikipedia.org/wiki/Factory_method_pattern
ビルダーデザインパターンのWikipediaエントリ:http : //en.wikipedia.org/wiki/Builder_pattern
ファクトリーは、コンストラクター(おそらく別のクラスのコンストラクター)のラッパー関数です。主な違いは、ファクトリメソッドパターンでは、オブジェクト全体を1つのメソッド呼び出しで構築し、すべてのパラメーターを1行で渡す必要があることです。最終的なオブジェクトが返されます。
一方、ビルダーパターンは、本質的に、コンストラクターの呼び出しに渡すことができるすべてのパラメーターの周りのラッパーオブジェクトです。これにより、セッターメソッドを使用してゆっくりとパラメーターリストを作成できます。ビルダークラスの追加のメソッドの1つにbuild()メソッドがあります。このメソッドは、ビルダーオブジェクトを目的のコンストラクターに渡し、結果を返します。
Javaのような静的言語では、パラメーターの数が少ない(オプションの可能性がある)場合に、パラメーターの可能なすべての組み合わせに対して伸縮自在のコンストラクターが不要になるため、これはより重要になります。また、ビルダーを使用すると、setterメソッドを使用して、コンストラクターの呼び出し後に直接変更できない読み取り専用フィールドまたはプライベートフィールドを定義できます。
基本的なファクトリーの例
// Factory
static class FruitFactory {
static Fruit create(name, color, firmness) {
// Additional logic
return new Fruit(name, color, firmness);
}
}
// Usage
Fruit fruit = FruitFactory.create("apple", "red", "crunchy");
基本的なビルダーの例
// Builder
class FruitBuilder {
String name, color, firmness;
FruitBuilder setName(name) { this.name = name; return this; }
FruitBuilder setColor(color) { this.color = color; return this; }
FruitBuilder setFirmness(firmness) { this.firmness = firmness; return this; }
Fruit build() {
return new Fruit(this); // Pass in the builder
}
}
// Usage
Fruit fruit = new FruitBuilder()
.setName("apple")
.setColor("red")
.setFirmness("crunchy")
.build();
次の2つのウィキペディアページのコードサンプルを比較する価値があるかもしれません。
http://en.wikipedia.org/wiki/Factory_method_pattern
http://en.wikipedia.org/wiki/Builder_pattern
Factoryパターンは、Builderパターンの簡略化されたバージョンとほぼ同じように見ることができます。
工場パターン、工場は、必要に応じて、オブジェクトの種々のサブタイプを作成を担当しています。
ファクトリメソッドのユーザーは、そのオブジェクトの正確なサブタイプを知る必要はありません。ファクトリメソッドの例は、または型指定されたオブジェクトをcreateCar
返すFord
場合がありHonda
ます。
ではビルダーパターン、異なるサブタイプはまた、ビルダーメソッドによって作成されますが、オブジェクトの組成は同じサブクラス内異なる場合があります。
車の例を続けるには、4シリンダーエンジンを備えた-typedオブジェクト、または6シリンダーを備えた-typedオブジェクトcreateCar
を作成するビルダーメソッドがあるとします。ビルダーパターンは、このより細かい粒度を可能にします。Honda
Honda
BuilderパターンとFactoryメソッドパターンの両方の図は、ウィキペディアで入手できます。
ビルダー設計パターンは、特定のタイプの別のオブジェクトをいくつかのステップで作成する方法を知っているオブジェクトを記述します。各中間ステップでターゲットアイテムに必要な状態を保持します。最終的な文字列を生成するためにStringBuilderが何を行うかを考えてください。
ファクトリデザインパターンは、特定のタイプが特定のパラメーターに基づいて選択される、1つのステップでいくつかの異なるが関連する種類のオブジェクトを作成する方法を知っているオブジェクトを記述します。シリアライザを作成すると、シリアライザが作成され、1回のロード呼び出しで必要なオブジェクトがすべて構築されます。
ステップごとの複雑なオブジェクトの作成:ビルダーパターン
単純なオブジェクトは、単一のメソッドを使用して作成されます:ファクトリメソッドパターン
複数のファクトリメソッドを使用してオブジェクトを作成する:抽象ファクトリパターン
BuilderパターンとFactoryパターンは、どちらもオブジェクトを作成するため、どちらも肉眼に非常に似ています。
この実際の例では、2つの違いをより明確にします。
ファストフードレストランに行き、Foodを注文したとします。
ピザ
トウガラシ、トマト、バーベキューチキン、パイナップルなし
したがって、さまざまな種類の食品がファクトリーパターンによって作成されますが、特定の食品のさまざまなバリアント(フレーバー)はビルダーパターンによって作成されます。
さまざまな種類の食品
ピザ、ハンバーガー、パスタ
ピザのバリエーション
チーズのみ、チーズ+トマト+トウガラシ、チーズ+トマトなど
どちらもオブジェクトを作成するための作成パターンです。
1)ファクトリーパターン-1つのスーパークラスとN個のサブクラスがあるとします。作成されるオブジェクトは、渡されるパラメーター/値によって異なります。
2)Builderパターン-複雑なオブジェクトを作成します。
Ex: Make a Loan Object. Loan could be house loan, car loan ,
education loan ..etc. Each loan will have different interest rate, amount ,
duration ...etc. Finally a complex object created through step by step process.
最初に、私の議論に続くいくつかの一般的なこと:
大きなソフトウェアシステムを設計する際の主な課題は、柔軟で変更が簡単なものでなければならないことです。このため、結合や凝集などのいくつかのメトリックがあります。システム全体を最初から再設計する必要なく、機能を簡単に変更または拡張できるシステムを実現するには、設計原則(SOLIDなど)を使用できます。しばらくして、一部の開発者は、これらの原則に従うと、同様の問題に対して適切に機能するいくつかの同様のソリューションがあることを認識しました。これらの標準ソリューションは、設計パターンであることが判明しました。
したがって、設計パターンは、凝集度の高い疎結合システムを実現するために、一般的な設計原則に従うことをサポートすることです。
質問に答える:
2つのパターンの違いを尋ねることで、どのパターンがシステムをどのように柔軟にするかを自問する必要があります。各パターンには、システム内のクラス間の依存関係を整理する独自の目的があります。
抽象ファクトリパターン: GoF:「具体的なクラスを指定せずに、関連オブジェクトまたは依存オブジェクトのファミリを作成するためのインターフェースを提供します。」
これはどういう意味ですか:この ようなインターフェイスを提供することで、各ファミリの製品のコンストラクタへの呼び出しがファクトリクラスにカプセル化されます。そして、これはシステム全体でこれらのコンストラクターが呼び出される唯一の場所であるため、新しいファクトリークラスを実装することでシステムを変更できます。別のファクトリーを通じてファクトリーの表現を交換すると、コードの大部分に触れることなく、製品のセット全体を交換できます。
ビルダーパターン: GoF:「複雑なオブジェクトの構築を表現から分離して、同じ構築プロセスで異なる表現を作成できるようにします。」
これはどういう意味ですか: 構築プロセスを、ディレクター(GoF)と呼ばれる別のクラスにカプセル化します。このディレクタには、製品の新しいインスタンスを作成するアルゴリズムが含まれています(たとえば、他のパーツから複雑な製品を作成する)。製品全体の不可欠な部分を作成するために、ディレクターはビルダーを使用します。directorでビルダーを交換することにより、同じアルゴリズムを使用して製品を作成できますが、単一パーツの表現(したがって、製品の表現)を変更できます。製品の表現でシステムを拡張または変更するには、新しいビルダークラスを実装するだけで済みます。
つまり、 Abstract Factory Patternの目的は、一緒に使用するために作成された一連の製品を交換することです。Builderパターンの目的は、製品を作成する抽象的なアルゴリズムをカプセル化して、製品のさまざまな表現に再利用することです。
私の意見では、抽象ファクトリー・パターンがビルダー・パターンの兄貴であるとは言えません。はい、どちらも作成パターンですが、パターンの主な目的はまったく異なります。
ビルダーとファクトリーの顕著な違いの1つは次のとおりです。
車があるとしましょう
class Car
{
bool HasGPS;
bool IsCityCar;
bool IsSportsCar;
int Cylenders;
int Seats;
public:
void Car(bool hasGPs=false,bool IsCityCar=false,bool IsSportsCar=false, int Cylender=2, int Seats=4);
};
上記のインターフェイスでは、次の方法で車を取得できます。
int main()
{
BadCar = new Car(false,false,true,4,4);
}
ただし、シートの作成中に例外が発生した場合はどうなりますか?オブジェクトはまったく取得されません//しかし、
次のような実装があるとします
class Car
{
bool mHasGPS;
bool mIsCityCar;
bool mIsSportsCar;
int mCylenders;
int mSeats;
public:
void Car() : mHasGPs(false), mIsCityCar(false), mIsSportsCar(false), mCylender(2), mSeats(4) {}
void SetGPS(bool hasGPs=false) {mHasGPs = hasGPs;}
void SetCity(bool CityCar) {mIsCityCar = CityCar;}
void SetSports(bool SportsCar) {mIsSportsCar = SportsCar;}
void SetCylender(int Cylender) {mCylenders = Cylender;}
void SetSeats(int seat) {mSeats = seat;}
};
class CarBuilder
{
Car* mCar;
public:
CarBuilder():mCar(NULL) { mCar* = new Car(); }
~CarBuilder() { if(mCar) { delete mCar; }
Car* GetCar() { return mCar; mCar=new Car(); }
CarBuilder* SetSeats(int n) { mCar->SetSeats(n); return this; }
CarBuilder* SetCylender(int n) { mCar->SetCylender(n); return this; }
CarBuilder* SetSports(bool val) { mCar->SetSports(val); return this; }
CarBuilder* SetCity(bool val) { mCar->SetCity(val); return this; }
CarBuilder* SetGPS(bool val) { mCar->SetGPS(val); return this; }
}
今、あなたはこのように作成することができます
int main()
{
CarBuilder* bp =new CarBuilder;
Car* NewCar = bp->SetSeats(4)->SetSports(4)->SetCity(ture)->SetGPS(false)->SetSports(true)->GetCar();
bp->SetSeats(2);
bp->SetSports(4);
bp->SetCity(ture);
bp->SetSports(true)
Car* Car_II= bp->GetCar();
}
ここで2番目のケースでは、1つの操作が失敗した場合でも車を取得します。
その車が後で完全に機能しないかもしれませんが、あなたは目的を持っているでしょう。
なぜなら、ファクトリーメソッドは車を1回の呼び出しで提供するのに対し、ビルダーは1つずつ構築するからです。
しかし、それはどちらに行くのかを決めるのニーズに依存します。
+-------------------------------------------------------------------+---------------------------------------------------+
| Builder | Factory |
+-------------------------------------------------------------------+---------------------------------------------------+
| Return only single instance to handle complex object construction | Retrun various instances on multiple constructors |
| No interface required | Interface driven |
| Inner classes is involved (to avoid telescopic constructors) | Subclasses are involved |
+-------------------------------------------------------------------+---------------------------------------------------+
類推:
BuilderとAbstract Factoryは異なる目的で使用されています。適切なユースケースに応じて、適切な設計パターンを選択する必要があります。
ビルダーの顕著な特徴:
ファクトリー(シンプルファクトリー)の主な機能:
多くの場合、設計はファクトリーメソッドを使用して開始し(それほど複雑ではなく、カスタマイズ可能で、サブクラスが急増します)、Abstract Factory、Prototype、またはBuilderに発展します(より柔軟でより複雑)に
関連する投稿を見てください:
ビルダーを別のクラスに維持する(流れるようなインターフェイス)
デザインパターン:ファクトリvsファクトリメソッドvs抽象ファクトリ
詳細については、以下の記事を参照してください。
Abstract FactoryとBuilderパターンはどちらもCreationalパターンですが、意図が異なります。
抽象ファクトリパターンは、関連オブジェクトのファミリのオブジェクト作成を強調します。
Builderパターンは、複雑なオブジェクトを段階的に構築することに焦点を当てています。複雑なオブジェクトを構築するプロセスから表現を分離するため、同じ構築プロセスを異なる表現に使用できます。
複雑な構造とは、構築されるオブジェクトが、抽象化によって表される他のさまざまなオブジェクトで構成される場合です。
マクドナルドのメニューを考えてみましょう。メニューにはドリンク、メイン、サイドが含まれています。個々の抽象化の子孫が一緒に構成されるかどうかに応じて、作成されたメニューには別の表現があります。
そこで、異なる表現を持つメニューの2つのインスタンスを取得しました。建設のプロセスは変わりません。ドリンク、メイン、サイドのメニューを作成します。
ビルダーパターンを使用して、複雑なオブジェクトを作成するアルゴリズムを、その作成に使用されるさまざまなコンポーネントから分離します。
ビルダーパターンに関しては、アルゴリズムはdirectorにカプセル化されますが、ビルダーは一体型パーツの作成に使用されます。他のパーツがメニューに構成されているため、directorのアルゴリズムで使用されるビルダーを変更すると、異なる表現になります。メニューの作成方法は同じです。
それらの主な違いは、Builderパターンが主に段階的に複雑なオブジェクトの作成を記述することです。Abstract Factoryパターンでは、objects-productsのファミリーに重点が置かれます。Builderは最後のステップで製品を返します。アブストラクトファクトリーパターンでは、製品はすぐに利用できます。
例:迷路を作成しているとしましょう
1.抽象ファクトリ:
Maze* MazeGame::CreateMaze (MazeFactory& factory) {
Maze* maze = factory.MakeMaze(); /// product is available at start!!
/* Call some methods on maze */
return maze;
}
2.ビルダー:
Maze* MazeGame::CreateMaze (MazeBuilder& builder) {
builder.buildMaze(); /// We don't have access to maze
/* Call some methods on builder */
return builder.GetMaze();
}
FactoryとBuilderのパターンの使用法と違いは、同じコードベースと要件の変更に取り組んでいるため、特定の期間でより簡単に理解/明確化できると思います。
私の経験では、通常、比較的複雑な初期化ロジックを主に隠すために、いくつかの静的なクリエーターメソッドを含むFactoryパターンから始めます。オブジェクト階層が複雑になると(または型、パラメーターを追加するにつれて)、メソッドにパラメーターが追加されてしまい、Factoryモジュールを再コンパイルする必要があることは言うまでもありません。これらすべてのものは、作成者メソッドの複雑さを増し、読みやすさを低下させ、作成モジュールをより脆弱にします。
このポイントは、おそらく遷移/拡張ポイントになります。そうすることで、構築パラメーターの周りにラッパーモジュールを作成します。実際の作成ロジックに手を加えることなく、いくつかの抽象化(おそらく)と実装を追加することで、新しい(類似した)オブジェクトを表すことができます。これで、複雑なロジックが「少なく」なりました。
率直に言って、私が直面したほとんどすべてのケースで両方の方法を使用できるので、唯一の多様性要因はそれらを区別するのに十分ではなかったので、「1ステップまたは複数のステップでオブジェクトを作成すること」がある種のことを指します今は何の利益も経験していません。これが私が最終的に考えたものです。
ファクトリー・パターンに対するビルダー・パターンの主な利点は、多くの可能なカスタマイズを備えた標準オブジェクトを作成したい場合ですが、通常はほんの少ししかカスタマイズしません。
たとえば、HTTPクライアントを作成する場合、デフォルトの書き込み/読み取りタイムアウト、プロトコル、キャッシュ、DNS、インターセプターなどのいくつかのデフォルトパラメーターを設定します。
クライアントのほとんどのユーザーは、これらのデフォルトのパラメーターを使用するだけですが、他の一部のユーザーは、他のパラメーターの一部をカスタマイズしたい場合があります。タイムアウトを変更して残りをそのまま使用したい場合もあれば、キャッシュなどのカスタマイズが必要な場合もあります。
クライアントをインスタンス化する方法(OkHttpClientから取得)は次のとおりです。
//just give me the default stuff
HttpClient.Builder().build()
//I want to use custom cache
HttpClient.Builder().cache(MyCache()).build()
//I want custom connection timeout
HttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS).build()
//I am more interested in read/write timeout
HttpClient.Builder()
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS).build()
これにファクトリーパターンを使用する場合、作成パラメーターの可能なすべての組み合わせを使用して多くのメソッドを作成することになります。ビルダーでは、気になるものを指定し、ビルダーに他のすべてのパラメーターの処理を任せます。
ビルドパターンは、 オブジェクトの作成の複雑さを強調します(「ステップ」で解決)
抽象パターンは、(複数だが関連する)オブジェクトの「抽象化」を「ちょうど」強調します。
違いは明らかですビルダーパターンでは、ビルダーは特定のタイプのオブジェクトを作成します。あなたはビルダーが何を構築しなければならないかを言わなければなりません。ファクトリパターンでは、抽象クラスを使用して、特定のオブジェクトを直接構築しています。
ここでビルダークラスは、メインクラスと特定の型クラスの間のメディエーターとして機能します。より抽象化。
どちらも非常によく似ていますが、オブジェクトの作成に多数のパラメーターがあり、その一部がデフォルト値でオプションである場合は、ビルダーパターンを使用します。
どちらのパターンも同じ必要性から来ています。複雑なオブジェクトの構築ロジックを一部のクライアントコードから隠します。しかし、何が「複雑」(または、時には複雑)なオブジェクトにするのでしょうか。主に、依存関係、またはより部分的な状態で構成されるオブジェクトの状態が原因です。コンストラクターによって依存関係を注入して初期オブジェクト状態を設定できますが、オブジェクトはそれらの多くを必要とする場合があり、デフォルトの初期状態になるものもあります(デフォルトの依存関係をnullに設定することは最もクリーンな方法ではないことを知っているはずだからです。 )および他のいくつかは、いくつかの条件によって駆動される状態に設定されます。さらに、何らかの「気付かない依存関係」であるオブジェクトプロパティがありますが、それらはオプションの状態をとることもできます。
その複雑さを支配する2つのよく知られた方法があります。
構成/集約:オブジェクトを作成し、その依存オブジェクトを作成してから、一緒に配線します。ここで、ビルダーは、コンポーネントの構築を導くルールを決定するプロセスを透過的かつ柔軟にすることができます。
ポリモーフィズム:構築ルールはサブタイプ定義に直接宣言されるため、サブタイプごとに一連のルールがあり、オブジェクトの構築にこれらのルールセットのどれを適用するかを決定する条件があります。工場はこのシナリオに完全に適合します。
これら2つのアプローチを混在させることを妨げるものは何もありません。製品のファミリーはビルダーで行われたオブジェクト作成を抽象化でき、ビルダーはファクトリーを使用してインスタンス化するコンポーネントオブジェクトを決定できます。
ビルダーと抽象ファクトリ
Builderの設計パターンは、ある程度、Abstract Factoryパターンに非常に似ています。そのため、どちらか一方を使用する場合に状況を区別できることが重要です。抽象ファクトリの場合、クライアントはファクトリのメソッドを使用して独自のオブジェクトを作成します。Builderの場合、Builderクラスはオブジェクトの作成方法を指示され、それを求められますが、クラスを組み合わせる方法はBuilderクラス次第であり、この詳細により2つのパターンに違いが生じます。
製品の共通インターフェース
実際には、具象ビルダーによって作成された製品の構造は大きく異なるため、異なる製品を派生させる理由がない場合は、共通の親クラスです。これはまた、Builderパターンを、一般的なタイプから派生したオブジェクトを作成するAbstract Factoryパターンと区別します。
ファクトリパターンは、実行時にクラスの具体的な実装を作成します。つまり、その主な目的は、ポリモーフィズムを使用して、サブクラスがインスタンス化するクラスを決定できるようにすることです。これは、コンパイル時には作成される正確なクラスがわからないことを意味しますが、ビルダーパターンは主に、コンストラクターのアンチパターンを伸縮する問題を解決することに関係します。これは、クラスの多数のオプションフィールドが原因で発生します。ビルダーパターンでは、コンパイル時に構築しようとしているオブジェクトがわかっているため、ポリモーフィズムの概念はありません。
これら2つのパターンの唯一の共通のテーマは、コンストラクターの非表示とファクトリーメソッドの背後にあるオブジェクトの作成、およびオブジェクトの構築を改善するためのビルドメソッドです。