blog

デザイン・パターン ファクトリー・パターン

ファクトリーパターンは通常ファクトリーメソッドパターンと呼ばれます。ファクトリーパターンの目的は、オブジェクトの作成と使用を分離することであり、いくつかのクラスのオブジェクトの作成と操作の動作の間の結...

Jan 14, 2021 · 12 min. read
シェア

ファクトリーパターンは、しばしばファクトリーメソッドパターンと呼ばれます。ファクトリーパターンの目的は、オブジェクトの作成と使用を分離することです、いくつかのクラスのオブジェクトの作成とオブジェクトの操作の動作の間の結合を削除するために使用されるので、クライアントクラスは、オブジェクトを作成するファクトリーにオブジェクトを作成しません。クラスがあまりにも多くの変数を持っていない場合は、建設も簡単ですが、ファクトリクラスを使用しないでください、それ以外の場合は、ファクトリの拡散につながるだけです。

ファクトリーパターンは作成パターンです。

モデルの背景

開発では、さまざまなオブジェクトを作成し、さまざまな条件に基づいて特定のメソッドを呼び出す必要がある状況がよくあります。たとえば、ユーザーのニーズに応じてさまざまなグラフィックを作成し、それを印刷するという要件を実装したいとします。ファクトリーパターンがなければ、コードは次のようになります:

public class BadCase {
 private String name;
 public BadCase(String shape) {
 if ("circle".equals(shape)) {
 this.name = "circle";
 } else if ("rect".equals(shape)) {
 this.name = "rect";
 } else if ("triangle".equals(shape)) {
 this.name = "triangle";
 }
 //else if ...
 }
 public void draw() {
 if ("circle".equals(this.name)) {
 System.out.println("i am circle");
 } else if ("rect".equals(this.name)) {
 System.out.println("i am rect");
 } else if ("triangle".equals(this.name)) {
 System.out.println("i am triangle");
 }
 //else if ...
 }
 public static void main(String[] args) {
 BadCase bad = new BadCase("rect");
 bad.draw();
 }
}

もちろん、これは意図的に最悪の書き方をしているわけで、完全にプロセス重視のこの文体には明らかにエレガンスがありません。彼の問題点は?

  1. オブジェクトの概念がない完全にプロセス重視のライティング。
  2. 作成時にif-elseで判定して作成し、出力時にまたif-elseで判定して出力するという、if-else判定が多すぎて、パフォーマンスが悪いだけでなく、コードが冗長になっています。
  3. この時点で五芒星の形を入れたい場合は、クラスを修正する必要があります。これは開閉の原則に反します。
  4. このBaseCaseというクラスは責任がオーバーロードされており、作成と使用の両方に責任があり、単一責任の原則に違反しています。
  5. メイン関数は、クライアントに相当し、クライアントは、作成するためにnewを使用する必要があり、すべてのイメージをしたい、あなたはBaseCaseインスタンスをnewする必要があります。実際には、新しいインスタンスは、コード内のオブジェクトの数を大量に作成するコードに結合され、初期化コードの束を使用するたびに結合する必要があります初期化作業の多くを必要とするかもしれない、オブジェクトの作成と使用が一緒に、コーディングの複雑さと強い結合です。

ファクトリーパターンはこの問題をある程度解決するためのもので、一般的に3つのファクトリーパターンがあります:

  • シンプルなファクトリーパターンで、静的ファクトリーメソッドパターンとしても知られています。

  • ファクトリーメソッドパターンは、ポリモーフィックファクトリーパターンとしても知られています;

  • Abstract Factoryパターンは、Toolboxパターンとしても知られています。

まず問題1を解いてください:

通常は上記のようには書きません。一般的には、あるタイプのオブジェクトに対応する各グラフィックになります。ファクトリーパターンの話なので、これらのオブジェクトをセットアップするのは一つの商品です:

//  A
class ProductA {
 public void printName() {
 System.out.println("i am A");
 }
}
//  B
class ProductB{
 public void printName() {
 System.out.println("i am B");
 }
}

Javaのプロジェクトでよくあることで、さまざまなエンティティ・クラスが大量に散在し、互いに関連しているプロジェクトです。ここでは、これら3つのファクトリーパターンを使って他の問題を解決する方法を紹介します。

シンプルなファクトリーパターン

シンプル・ファクトリー・パターンはGoFの23のデザイン・パターンには属していませんが、他のファクトリー・パターンを学ぶための基礎となるものです。主な目的はオブジェクトの作成と使用を分離することです。使用されるすべてのオブジェクトはファクトリクラスを通して作成され、次にクライアントが使用します。

ユーエムエル

理論

  1. 作成する必要のあるオブジェクトは、製品クラスとして設定されたさまざまなクラスにカプセル化されます。
  2. これらの製品クラスの共通部分は抽象化され、抽象製品クラスにカプセル化される必要があります。
  3. 渡されたパラメーターに基づいてさまざまなオブジェクトを作成する静的メソッドを持つファクトリークラスを提供します。

ものだね

  1. 製品の抽象インターフェースまたはクラス
  2. ファクトリークラス
  3. 静的ファクトリーメソッド

気付く

最初に製品クラスを抽象化

// ファクトリー内のすべての製品は、インターフェイスを抽象化する。製品の拡張が容易で、ファクトリクラスとクライアントクラスの変更に影響を与えない。
interface Product {
 void printName();
}
//  A
class ProductA implements Product {
 @Override
 public void printName() {
 System.out.println("i am A");
 }
}
//  B
class ProductB implements Product {
 @Override
 public void printName() {
 System.out.println("i am B");
 }
}

ファクトリークラスの実装

public class SimpleFactory {
 	// 静的ファクトリーメソッド
 public static Product getProduct(String name) {
 switch (name.toLowerCase()) {
 case "a":
 return new ProductA();
 case "b":
 return new ProductB();
 default:
 return null;
 }
 }
	// クライアントサイドでの使用をシミュレートする
 public static void main(String[] args) {
 SimpleFactory.getProduct("a").printName();
 }
}

長所と短所

バンテージ

  • オブジェクトの作成と使用の分離:作成はファクトリークラスによって行われ、クライアントはそれを使用するだけです。
  • ある程度、クラスの責任は軽減され、オブジェクトの使用はオブジェクトの内部にカプセル化されます。

欠点

  • ファクトリークラスはまだ責任を負わされたままオーバーロードされています:まだ判断が多すぎますし、正しく動作しなければそのファクトリーのすべてのオブジェクトが影響を受けます。
  • システムを拡張する場合は、ファクトリークラスを修正する必要があります。製品を追加する場合は、ファクトリークラスのコードも修正する必要があります。
  • 静的ファクトリーメソッドを使用するため、ファクトリークラスは継承ベースの階層を形成できません。
  • ファクトリークラスを導入する必要があるため、システムの複雑さが増す可能性があります。

アプリケーションのシナリオ

  • ファクトリークラスは、より少ないオブジェクトを作成する役割を担います。なぜなら、オブジェクトの数が少なければ、ファクトリークラス内部のビジネスもそれほど複雑ではないからです。
  • クライアントはオブジェクトを取得するためのパラメータを渡したいだけで、オブジェクトがどのように作成されるかは気にしません。

ヒント

パラメータを渡す必要があるファクトリクラスをクライアントで使用すると、パラメータを変更するたびにクライアントのコードを修正する必要があり、気分が悪いかもしれません。この問題は、設定ファイルを読み込んでパラメータを渡すことで解決できます。これはまさにSpringのアイデアです。

ファクトリーメソッドパターン

単純なファクトリーパターンには問題があります。新しい製品を導入する場合、ファクトリークラスを変更する必要があり、オープンとクローズの原則に反するのです。ファクトリーメソッドパターンは、この問題を解決するための単純な拡張です。その目的は、ファクトリーの責任が多すぎるという問題を解決することです。

考えてみてください。元のコードを修正する代わりに、動的にクラスを追加してファクトリークラスを修正したい場合、どうしますか?その通りです!別の抽象化レイヤーを抽出し、サブクラスを追加するだけで動的な拡張が完了します。ファクトリーメソッドパターンもこの考えに基づいており、ファクトリーを動的に拡張することができます。

ユーエムエル

理論

上記では製品クラスを抽象化することに焦点を当てましたが、ファクトリーメソッドパターンはファクトリーも抽象化することに焦点を当てます

ファクトリーのファクトリーメソッドは、抽象ファクトリークラスのレイヤーに抽象化されます。具体的な製品の作成はファクトリーのサブクラスに任されます。

各製品クラスは、後でその製品クラスのオブジェクトを作成するために使用される具体的なファクトリ実装クラスに対応しています。

ファクトリメソッドは、さらに責任を軽減するために、ファクトリクラスの単純なファクトリパターンに相当する:彼らは自分の製品に責任があるように、より多くの工場を作成するには、クライアントは、製品に相当する抽象的なファクトリクラスの層に包まれて使用する、直接指向抽象ファクトリプログラミング。クライアントは、製品のクラスを知っている必要はありませんが、唯一の行の工場クラスの製品を知っている必要があります。その後、製品のクラスとクライアントの分離。あなたは、行のファクトリコードを照会することができます。

ものだね

  1. 製品と製品の抽象化
  2. 工場の抽象化
  3. 具体的なファクトリー、各製品に1つのファクトリー、ファクトリーの具体的な実装クラス。

気付く

// 抽象ファクトリー
interface ProductFactory {
 Product getProduct();
}
// プロダクションAのための抽象ファクトリー
class AProductFactory implements ProductFactory {
 @Override
 public Product getProduct() {
 return new ProductA();
 }
}
// プロダクションBのための抽象ファクトリー
class BProductFactory implements ProductFactory {
 @Override
 public Product getProduct() {
 return new ProductB();
 }
}
// クライアント側での使用
ProductFactory factory = new AProductFactory(); // 設定ファイルを使用して、ロードするファクトリーを指定できる
Product product = factory.getProduct();
product.printName();

このパターンでは、商品を追加するときにそのコードは必要ありませんが、多くのクラスを追加する必要があります。

長所と短所

バンテージ

  • オープニングとクロージングの原則により沿って、新しい追加はファクトリークラスの変更を必要としません。
    • 新しく追加された製品に対応するファクトリクラスを追加し、それをプロジェクトに追加するだけです。
  • 静的メソッドがなければ、ファクトリーは継承システムに基づいて形成することができます。
  • 各製品は、1つの義務に合致した1つの製品だけを作る責任があります。

欠点

  • 製品を追加するには、対応するファクトリークラスを追加する必要があります!
  • ファクトリクラスのコードを変更する必要はありませんが、新しく追加されたファクトリ実装クラスは、クライアントによってロードされる具体的な実装クラスを指定するために設定する必要があります。そのため、クライアント側のコードでは一般的にDOMやリフレクションなどの技術を使用して特定のインスタンス化の設定をサポートする必要があり、システムを理解する難易度が上がります。
    • 注:開閉の原則は、何も変更されないという意味ではありません!これは不可能です!
    • 変更しないでください部分のカプセル化でなければならず、ユーザーのクライアント側はまだ変更する必要があります。あなたが変更することはできません理由は、想像してみて、あなたが関数を記述すると、他の人が使用するためのサードパーティ製のパッケージの形で、一度問題を変更すると、あなたのサードパーティ製のパッケージを使用してすべての人々の影響は、あなたが変更し、変更するためにそれらを行かせ、明らかに不可能なので、クラスのこの性質のためのサードパーティ製のパッケージのために、唯一の変更を許可しない追加することができます。

アプリケーションのシナリオ

  • ** このパターンはファクトリーメソッドで最もよく使われるパターンです!**一般的に、このパターンを使うことが適切かどうか試してみるべきです。
  • 製品クラスに関する情報をクライアントに渡す代わりに、オブジェクトの作成に特化したファクトリ・クラスのみをクライアントに認識させます。つまり、作成はファクトリーレベルでカプセル化され、使用は製品エンティティレベルでカプセル化されます。

抽象ファクトリーパターン

上記のファクトリーメソッドパターンでは、システム内に多数のファクトリークラスが存在することになります。関連する多数の製品で製品グループを形成し、それを同じ工場に与えて統一生産することが可能です。

ソート

  • 新しい製品を追加するには、大量のクラスを追加する必要があります。
  • 同じスタイルの製品で、すべてを結びつけることができればいいのですが。

ユーエムエル

理論

まず、2つのコンセプトを理解しましょう:

**製品階層:***製品階層は、製品の継承構造であり、例えば、抽象クラスはテレビであり、そのサブクラスはハイアールTV、ハイセンスTV、TCL TVであり、抽象テレビと特定のブランドのテレビは、抽象テレビを親クラスとし、特定のブランドのテレビをサブクラスとする製品階層を構成します。

例えば、ハイアール電器工場が生産するハイアールTVとハイアール冷蔵庫は、ハイアールTVはTVの製品階層にあり、ハイアール冷蔵庫は冷蔵庫の製品階層にありますハイアールTV、ハイアール冷蔵庫は製品ファミリーです。

上記の2つのコンセプトを理解できなくても問題ないと思いますし、表現を抑える必要もありません。考え方としては、システム内にいくつかのクラスの製品があり、同時にいくつかのスタイルがあるということです。各スタイルは工場として扱うことができ、この工場はそのスタイルの下にあるすべてのクラスのすべての製品を生産します。

抽象ファクトリーパターンは、ファクトリーメソッドパターンのファクトリークラスをさらに縮小したものです。製品のファミリーを束ねて製品ファミリーを形成します。そうすれば、製品ファミリーのファクトリークラスだけで十分です。

ものだね

  1. 商品の抽象化。そして、これらの抽象化されたものに対する具体的な製品。 例えば、UIのボタンは抽象化され、赤いボタンと黄色いボタンは具体的な実装です。
  2. 製品ファミリーのファクトリー抽象化。そして具体的な実装。たとえば:ボタン、テキストボックス、選択ボックス、製品ファミリとしてこれらの3つは、ファクトリの抽象化は、これらの3つの抽象メソッドを作成する必要があります。製品ファミリでは、共通の制約を持つ必要があるなど、上記のこれらのコントロールは、異なるUIに基づいている別の変更があるでしょう。

気付く

製品ファミリー

すべてのクラスが満席の場合:タイプ * スタイル数

//A1 A2 とAは異なる製品クラスである。 Aは特定の商品である A1 A2はその商品の異なるスタイルである
interface Product_A {}
//A1 B1 それは製品ファミリーである
class ProductA1 implements Product_A {}
class ProductA2 implements Product_A {}
//....
//B  
interface Product_B {}
class ProductB1 implements Product_B {}
class ProductB2 implements Product_B {}

抽象化ファクトリー

トップレベルの抽象化とは、あらゆる種類の抽象化の創造を含むものです。

コンクリート工場の工場方式は、そのスタイルに該当するすべての種類の製品を作って行きます。

// 商品ファミリーのファクトリー抽象化
interface IAbstractFactory {
 Product_A createA();
 Product_B createB();
}
// 製品ファミリー1を持つすべての製品
class Factory1Impl implements IAbstractFactory {
 @Override
 public Product_A createA() {
 return new ProductA1();
 }
 @Override
 public Product_B createB() {
 return new ProductB1();
 }
}
// 製品ファミリー2を持つすべての製品
class Factory2Impl implements IAbstractFactory {
 @Override
 public Product_A createA() {
 return new ProductA2();
 }
 @Override
 public Product_B createB() {
 return new ProductB2();
 }
}

クライアント

public static void main(String[] args) {
 IAbstractFactory factory = new Factory1Impl();
 Product_A a = factory.createA();
 Product_B b = factory.createB();
 a.printName();
 b.printName();
}

長所と短所

バンテージ

  • ファクトリーメソッドパターンでファクトリークラスの数を減らします:複数のファクトリーを特徴に基づいて1つのファクトリーにまとめます。そして、1つのファクトリーでこれらのオブジェクトを作成します。
  • 新しい製品ファミリーを簡単に追加可能

欠点

  • 新しい製品を追加するのは大変な作業で、多くのコード変更が必要になるかもしれません。

アプリケーションのシナリオ

まず、ビジネスではファクトリーパターンを使ってオブジェクトを作成する必要があります。そして、これらのオブジェクトにはもう1つの特徴があります。次に、抽象ファクトリーパターンの使用を検討します。

  • UIのスキニング、コンポーネントのレイアウトは同じで、それぞれの小さなコントロールのUIが異なるだけで、このレイアウトはファミリーです。(このモデルは、ファミリーという概念に大きな価値を置いています)。
  • 異なるOS用のプログラムを生成します。OSのAPIレイヤーの呼び出しが異なるだけで、どれも同じように見えます。

概要

速記

これらの工場パターンを大まかに整理して覚えておくと便利:

この3つとも、製品に抽象化レイヤーを必要とします。

  • シンプル・ファクトリー
    • 静的なファクトリーメソッドを持つファクトリークラスです。
    • ファクトリーメソッドは、if-else または switch-case を使用してオブジェクトを NEW するために使用されます。
  • ファクトリーメソッド
    • 多くのファクトリー実装クラスでファクトリーをポンピングし、各ファクトリー実装クラスは製品を作成します。
    • 1つの製品に1つのファクトリークラス、各ファクトリークラスは1つの製品に対応し、ファクトリーメソッドはNEWオブジェクト。
  • 抽象ファクトリー
    • ファクトリ実装クラスは、スタイルに属するさまざまなタイプの製品の生産に特化されています。
    • スタイルはファクトリ クラスに対応し、各ファクトリ クラスは複数の製品に対応し、複数の新しいオブジェクト ファクトリ メソッドを提供します。

付く

関連コード:

また、コードや記事に問題があれば修正してください!ありがとうございました!

Read next

MockitoフレームワークのモックVoidメソッド

コードを書くとき、常にvoidを返すメソッドがあり、テストケースのどこかの時点でvoidメソッドをエミュレートする必要があります。では、どうすればよいのでしょうか?次のコンテンツでは、Mockitoを使ってこのニーズを満たすために一緒に取り組みましょう。 Mockitoはユニットテストを書くために使われる最も有名なモッキングフレームワークの1つです。 あるメソッドAがあって、その中で別のvoidメソッドBが使われているとします。

Jan 14, 2021 · 5 min read