この時点で、おそらくすでにデザイン パターンに遭遇しているでしょう。たとえば、シングルトン

パターンとは何か、パターンが必要な理由、作成パターンとは何か (シングルトンがその例です) を思い出してみましょう。新しいパターンであるファクトリーメソッドについても学習します。

ソフトウェア開発において、デザイン パターンは、繰り返し発生するコンテキスト内でのデザイン上の問題に対する解決策を表す、繰り返し可能なアーキテクチャ構造です。

通常、パターンはコードに直接変換できる最終的なソリューションではありません。これは、さまざまな状況で使用できる問題の模範的な解決策にすぎません。

作成パターンは、オブジェクトの作成プロセスを扱うデザイン パターンです。これらにより、オブジェクトの作成、構成、表示に使用される方法に依存しないシステムを作成できます。

ファクトリメソッドは、親クラスでオブジェクトを作成するための共通インターフェイスを定義する作成デザイン パターンであり、その子孫にこれらのオブジェクトを作成する機能を与えます。作成時に、子孫はどのクラスを作成するかを決定できます。

パターンはどのような問題を解決しますか?

配信プログラムを作成することにしたと想像してください。最初は、車を備えた宅配業者を雇い、プログラム内の配送車両を表すオブジェクト。宅配便は、A 地点から B 地点などに荷物を配達します。簡単にピーシー。

このプログラムは人気を集めています。ビジネスが成長しており、新しい市場に拡大したいと考えています。たとえば、食品の配達や貨物の配送も始めることができます。この場合、食品は宅配業者によって徒歩、スクーター、自転車で配達できますが、貨物輸送にはトラックが必要です。

次に、各宅配便が運ぶことができる量を含む、いくつかの事項 (いつ、誰に、何を、どれくらい配達するか) を追跡する必要があります。新しい交通手段は、速度や容量が異なります。次に、プログラム内のほとんどのエンティティが、クラス。プログラムを他の配信方法で動作させるには、既存のコード ベースを書き換える必要があり、新しい車両を追加するたびにそれを繰り返す必要があることに気づきました。

その結果、交通手段の種類に応じて異なるアクションを実行する条件文が満載された恐ろしいコードが作成されます。

ソリューション

ファクトリ メソッド パターンは、new演算子を直接使用するのではなく、特別なファクトリ メソッドを呼び出すことによってオブジェクトを作成することを提案します。ファクトリ メソッドを持つクラスのサブクラスは、特定の車両の作成されたオブジェクトを変更できます。一見すると、これは無意味に思えるかもしれません。コンストラクター呼び出しをプログラム内のある場所から別の場所に移動しただけです。ただし、サブクラスのファクトリ メソッドをオーバーライドして、作成される交通手段の種類を変更できるようになりました。

このアプローチのクラス図を見てみましょう。

このシステムが機能するには、返されるすべてのオブジェクトが共通のインターフェイスを持っている必要があります。サブクラスは、このインターフェイスを実装するさまざまなクラスのオブジェクトを生成できます。

たとえば、Truck クラスCarクラスは、deliverメソッドを使用してCourierTransportインターフェイスを実装します。これらの各クラスは、異なる方法でメソッドを実装します。つまり、トラックは貨物を配達し、自動車は食品や荷物などを配達します。TruckCreatorクラスのファクトリ メソッドはトラック オブジェクトを返し、CarCreatorクラスは車のオブジェクトを返します。

ファクトリ メソッドのクライアントの場合、これらのオブジェクトはある種の抽象CourierTransportとして扱われるため、これらのオブジェクト間に違いはありません。クライアントは、オブジェクトが配信するためのメソッドを持っていることを非常に気にしますが、そのメソッドが正確にどのように機能するかは重要ではありません。

Java での実装:


public interface CourierTransport {
	void deliver();
}
public class Car implements CourierTransport {
	@Override
	public void deliver() {
    		System.out.println("The package is being delivered by car");
	}
}
public class Truck implements CourierTransport {
	@Override
	public void deliver() {
    		System.out.println("The freight is being delivered by truck");
	}
}
public abstract class CourierTransportCreator {
	public abstract CourierTransport createTransport();
}
public class CarCreator extends CourierTransportCreator {
	@Override
	public CourierTransport createTransport() {
    		return new Car();
	}
}
public class TruckCreator extends CourierTransportCreator {
	@Override
	public CourierTransport createTransport() {
    		return new Truck();
	}
}
 
public class Delivery {
	private String address;
	private CourierTransport courierTransport;
 
	public void Delivery() {
	}
 
	public Delivery(String address, CourierTransport courierTransport) {
    	this.address = address;
    	this.courierTransport = courierTransport;
	}
 
	public CourierTransport getCourierTransport() {
    		return courierTransport;
	}
 
	public void setCourierTransport(CourierTransport courierTransport) {
    		this.courierTransport = courierTransport;
	}
 
	public String getAddress() {
    		return address;
	}
 
	public void setAddress(String address) {
    		this.address = address;
	}
}
public static void main(String[] args) {
    	// Accept a new type of order from the database (pseudocode)
    	String type = database.getTypeOfDeliver();
 
    	Delivery delivery = new Delivery();
    	
    	// Set the transport for delivery
        delivery.setCourierTransport(getCourierTransportByType(type));
    	
    	// Make the delivery
        delivery.getCourierTransport().deliver();
 
	}
 
	public static CourierTransport getCourierTransportByType(String type) {
    	switch (type) {
        	case "CarDelivery":
            	return new CarCreator().createTransport();
        	case "TruckDelivery":
            	return new TruckCreator().createTransport();
        	default:
            	throw new RuntimeException();
	    }
	}
    

新しい配信オブジェクトを作成する場合、プログラムは適切なトランスポート オブジェクトを自動的に作成します。

このパターンをいつ適用すればよいでしょうか?

1. コードで処理する必要があるオブジェクトのタイプと依存関係が事前にわからない場合。

ファクトリ メソッドは、交通手段を作成するコードを、交通手段を使用するコードから分離します。その結果、オブジェクトを作成するコードは、コードの残りの部分に手を加えることなく拡張できます。

たとえば、新しいタイプのトランスポートのサポートを追加するには、新しいサブクラスを作成し、その中に新しいトランスポートのインスタンスを返すファクトリ メソッドを定義する必要があります。

2. 新しいオブジェクトを作成するのではなく、既存のオブジェクトを再利用してシステム リソースを節約したい場合。

この問題は通常、データベース接続やファイル システムなど、リソースを大量に消費するオブジェクトを操作するときに発生します。

既存のオブジェクトを再利用するために必要な手順を考えてみましょう。

  1. まず、作成したすべてのオブジェクトを保存するための共有リポジトリを作成する必要があります。

  2. 新しいオブジェクトをリクエストするときは、リポジトリを調べて、使用可能なオブジェクトがリポジトリに含まれているかどうかを確認する必要があります。

  3. オブジェクトをクライアント コードに返します。

  4. ただし、使用可能なオブジェクトがない場合は、新しいオブジェクトを作成してリポジトリに追加します。

このすべてのコードは、クライアント コードが乱雑にならない場所に配置する必要があります。これらすべてのチェックはオブジェクトの作成時にのみ必要となるため、最も便利な場所はコンストラクターです。残念ながら、コンストラクターは常に新しいオブジェクトを作成します。既存のオブジェクトを返すことはできません。

つまり、既存のオブジェクトと新しいオブジェクトの両方を返すことができる別のメソッドが必要になります。これがファクトリーメソッドになります。

3. ユーザーがフレームワークまたはライブラリの一部を拡張できるようにしたい場合。

ユーザーは継承を通じてフレームワーク クラスを拡張できます。しかし、フレームワークに標準クラスではなくこれらの新しいクラスのオブジェクトを作成させるにはどうすればよいでしょうか?

解決策は、ユーザーがコンポーネントだけでなく、それらのコンポーネントを作成するクラスも拡張できるようにすることです。このためには、作成クラスに定義できる特定の作成メソッドが必要です。

利点

  • クラスを特定のトランスポート クラスから分離します。
  • 交通手段を作成するためのコードを 1 か所に保管し、コードの保守が容易になります。
  • 新しい交通手段をプログラムに簡単に追加できます。
  • オープンクローズの原則を実装します。

短所

各製品クラスには独自の作成者サブクラスが必要なため、大規模な並列クラス階層が発生する可能性があります。

要約しましょう

ファクトリ メソッド パターンについて学び、考えられる実装の 1 つを確認しました。このパターンは、他のオブジェクトを作成するためのオブジェクトを提供するさまざまなライブラリでよく使用されます。

メインのビジネス ロジックと対話し、さまざまなコンテキストによってコードが肥大化しないようにするために、既存のクラスのサブクラスの新しいオブジェクトを簡単に追加する場合は、ファクトリ メソッド パターンを使用します。