デザインパターン_クリエイティブパターン-《ビルダーパターン》
ダークホースプログラマによるJavaデザインパターンの詳細解説、23のJavaデザインパターン(図表+フレームワークソースコード解析+実戦)をノートとしてまとめています。
概要
複雑なオブジェクトの構築と表現を分離して、同じ構築プロセスで異なる表現を作成できるようにします。
- 部品の構築(ビルダーが責任)と組み立て(ディレクターが責任)は分離されています。したがって、複雑なオブジェクトを構築できます。このパターンは、オブジェクトの構築プロセスが複雑な状況に適しています。
- 建設と組立が分離されるため。異なるビルダーと同じアセンブリで異なるオブジェクトを作成できます。また、同じビルダーで異なるアセンブリ シーケンスで異なるオブジェクトを作成することもできます。つまり、構築アルゴリズムとアセンブリアルゴリズムの分離が実現され、より良い再利用が実現されます。
- ビルダー パターンを使用すると、パーツを組み立てプロセスから分離し、複雑なオブジェクトを段階的に作成できます。ユーザーは、内部構造の詳細を知らなくても、複雑なオブジェクトのタイプを指定するだけでオブジェクトを取得できます。
構造
ビルダー パターンには次の役割が含まれます。
-
抽象ビルダー クラス (Builder): このインターフェイスは、複合オブジェクトのこれらの部分の作成を規定しており、特定のコンポーネント オブジェクトの作成には関与しません。
-
Concrete Builder クラス (ConcreteBuilder): 複雑な製品の各コンポーネントの特定の作成メソッドを完了するための Builder インターフェイスを実装します。構築プロセスが完了すると、製品のインスタンスが提供されます。
-
製品クラス (Product): 作成される複合オブジェクト。
-
Director クラス (Director): 特定のビルダーを呼び出して、複雑なオブジェクトの各部分を作成します。ディレクターは、特定の製品の情報には関与せず、オブジェクトの部分が完全に、または特定の方法で作成されることを保証することのみを担当します。注文。
クラス図は次のとおりです。
例
シェア自転車を作成する
自転車の製造は、フレームやサドルなどの部品の製造を含む複雑なプロセスです。フレームはカーボンファイバーやアルミニウム合金などで作られ、シートはゴムや革などで作られています。自転車の製造には、ビルダー パターンを使用できます。
ここで、Bike はフレームやシートなどのコンポーネントを含む製品、Builder は抽象的なビルダー、MobikeBuilder と OfoBuilder は具体的なビルダー、Director は指揮官です。クラス図は次のとおりです。
具体的なコードは次のとおりです。
-
商品カテゴリー(自転車)
public class Bike { private String frame; private String seat; public String getFrame() { return frame; } public void setFrame(String frame) { this.frame = frame; } public String getSeat() { return seat; } public void setSeat(String seat) { this.seat = seat; } }
-
抽象ビルダークラス
public abstract class Builder { // 声明Bike类型的变量,并进行赋值(提高复用性) protected Bike mBike = new Bike(); public abstract void buildFrame(); public abstract void buildSeat(); public abstract Bike createBike(); }
-
コンクリートビルダークラス(Mobike Builderクラス)
public class MobikeBuilder extends Builder { @Override public void buildFrame() { mBike.setFrame("铝合金车架"); } @Override public void buildSeat() { mBike.setSeat("真皮车座"); } @Override public Bike createBike() { return mBike; } }
-
コンクリートビルダークラス(ofo自転車ビルダークラス)
public class OfoBuilder extends Builder { @Override public void buildFrame() { mBike.setFrame("碳纤维车架"); } @Override public void buildSeat() { mBike.setSeat("橡胶车座"); } @Override public Bike createBike() { return mBike; } }
-
コマンダークラス
public class Director { // 声明builder类型的变量 private Builder mBuilder; public Director(Builder builder) { mBuilder = builder; } // 组装自行车的功能 public Bike construct() { mBuilder.buildFrame(); mBuilder.buildSeat(); return mBuilder.createBike(); } }
-
テストクラス
public class Client { public static void main(String[] args) { showBike(new OfoBuilder()); showBike(new MobikeBuilder()); } private static void showBike(Builder builder) { // 创建指挥者对象 Director director = new Director(builder); // 让指挥者指挥组装自行车 Bike bike = director.construct(); System.out.println(bike.getFrame()); System.out.println(bike.getSeat()); } }
出力
铝合金车架 真皮车座 碳纤维车架 橡胶车座
知らせ:
-
上の例は、ビルダー モードの通常の使用法です。ディレクター クラス Director は、ビルダー モードで非常に重要な役割を果たします。これは、特定のビルダーに製品のビルド方法、呼び出し順序の制御方法、および完全な結果を返す方法をガイドするために使用されます。 product クラスを呼び出し元に渡しますが、システム構造を簡素化する必要がある場合には、 を置くことができます
指挥者类和抽象建造者进行结合
。// 抽象builder类 public abstract class Builder { protected Bike mBike = new Bike(); public abstract void buildFrame(); public abstract void buildSeat(); public abstract Bike createBike(); public Bike construct() { this.buildFrame(); this.BuildSeat(); return this.createBike(); } }
例証します:
- これにより、システム構造が非常に単純化されますが、抽象コンストラクター クラスの責任も増加し、単一責任の原則には準拠しません。construct() が複雑すぎる場合は、Director でカプセル化することをお勧めします。
長所と短所
アドバンテージ
- ビルダー パターンのカプセル化は非常に良好です。ビルダー モードを使用すると、変更を効果的にカプセル化できます。ビルダー モードを使用するシナリオでは、一般的なプロダクト クラスとビルダー クラスが比較的安定しています。そのため、主要なビジネス ロジックをコマンダー クラスでカプセル化することで、全体的に比較的良好な安定性を実現できます。
- ビルダー モードでは、クライアントは製品の内部構成の詳細を知る必要がなく、製品自体は製品作成プロセスから切り離されているため、同じ作成プロセスで異なる製品オブジェクトを作成できます。
- 製品の作成プロセスをより細かく制御できます。複雑な製品の作成ステップをさまざまな方法に分解すると、作成プロセスがより明確になり、プログラムを使用して作成プロセスを制御するのがより便利になります。
- ビルダー パターンは拡張が簡単です。新しい要件が発生した場合、基本的に以前にテストされたコードを変更することなく、新しいビルダー クラスを実装するだけで要件を完了できるため、元の機能にリスクが発生することはありません。開閉の原則に従ってください。
欠点がある
- 一般に、ビルダー モードで作成される製品は共通点が多く、コンポーネントも類似していますが、製品が大きく異なる場合はビルダー モードの使用には適さないため、使用範囲が制限されます。
使用するシーン
Builder パターンは複雑なオブジェクトを作成するため、製品のさまざまな部分が大幅な変更に直面することがよくありますが、それらを組み合わせるアルゴリズムは比較的安定しているため、通常は次のような状況で使用されます。
- 作成されたオブジェクトは複雑で複数のパーツから構成されており、各パーツは複雑な変化に直面しますが、コンポーネント間の構築順序は安定しています。
- 複雑なオブジェクトを作成するためのアルゴリズムは、そのオブジェクトの構成要素やそれらがどのように組み立てられるかには依存しません。つまり、製品の構築プロセスと最終的な表現は独立しています。
スキーマ拡張
上記の用途に加えて、ビルダー モードには開発でも一般的な用途があります。つまり、クラス コンストラクターが多くのパラメーターを渡す必要がある場合、このクラスのインスタンスが作成されると、コードの可読性が非常に低くなります。エラーが発生しやすいため、現時点ではビルダー パターンをリファクタリングに使用できます。
-
リファクタリング前のコードは次のとおりです。
// 手机类 public class Phone { private String cpu; private String screen; private String memory; private String mainboard; public Phone(String cpu, String screen, String memory, String mainboard) { this.cpu = cpu; this.screen = screen; this.memory = memory; this.mainboard = mainboard; } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public String getScreen() { return screen; } public void setScreen(String screen) { this.screen = screen; } public String getMemory() { return memory; } public void setMemory(String memory) { this.memory = memory; } public String getMainboard() { return mainboard; } public void setMainboard(String mainboard) { this.mainboard = mainboard; } @Override public String toString() { return "Phone{" + "cpu='" + cpu + '\'' + ", screen='" + screen + '\'' + ", memory='" + memory + '\'' + ", mainboard='" + mainboard + '\'' + '}'; } } public class Client { public static void main(String[] args) { // 构建Phone对象 Phone phone = new Phone("intel", "三星屏幕" ,"金士顿", "华硕"); System.out.println(phone); } }
上記の例では、クライアント コードで Phone オブジェクトを構築し、4 つのパラメーターを渡しています。さらにパラメーターがある場合はどうなるでしょうか。コードの可読性と使用コストは比較的高くなります。
-
リファクタリング後のコード:
// 手机类 public class Phone { private String cpu; private String screen; private String memory; private String mainboard; // 私有构造方法,传递一个建造者 private Phone(Builder builder) { cpu = builder.cpu; screen = builder.screen; memory = builder.memory; mainboard = builder.mainboard; } // 内部类-建造者类 public static final class Builder { private String cpu; private String screen; private String memory; private String mainboard; public Builder() { } public Builder cpu(String val) { cpu = val; return this; } public Builder screen(String val) { screen = val; return this; } public Builder memory(String val) { memory = val; return this; } public Builder mainboard(String val) { mainboard = val; return this; } // 使用构建者创建Phone对象 public Phone build() { return new Phone(this); } } @Override public String toString() { return "Phone{" + "cpu='" + cpu + '\'' + ", screen='" + screen + '\'' + ", memory='" + memory + '\'' + ", mainboard='" + mainboard + '\'' + '}'; } } // 测试类 public class Client { public static void main(String[] args) { // 创建手机对象 通过构建者对象获取手机对象 Phone phone = new Phone.Builder() .cpu("intel") .mainboard("华硕") .memory("金士顿") .screen("三星") .build(); System.out.println(phone); } }
リファクタリングされたコードは使いやすくなり、コードの可読性が向上し、開発効率もある程度向上します。
本来のビルダーモードの構築順序はコンダクタークラスによって決定されていましたが、現在は構築順序がクライアントに引き継がれています。
ソフトウェア設計の観点から見ると、プログラマーに対する要求は比較的高いです。
要約する
クリエーターモードの比較
ファクトリ メソッド パターンとビルダー パターン
- ファクトリ メソッド パターンはオブジェクト全体の作成に焦点を当てますが、ビルダー パターンはコンポーネント構築のプロセスに焦点を当て、段階的に正確な構築を通じて複雑なオブジェクトを作成することを目的としています。
- 両者の違いを説明するために簡単な例を挙げてみましょう。スーパーマンを作りたい場合、ファクトリーメソッドモデルを使用すると、無限の力、飛行能力、下着を着用する能力を備えたスーパーマンが直接生成されます。ビルダーモデルは、手、頭、足、胴体などのパーツを組み立て、外で下着を着用することでスーパーマンが誕生します。
抽象的なファクトリー パターン VS ビルダー パターン
- 抽象ファクトリー パターンは、製品ファミリーの作成を実現します。製品ファミリーとは、さまざまな分類次元を持つ製品の組み合わせである一連の製品です。抽象ファクトリー パターンでは、構築プロセスを考慮する必要はなく、どの製品がどの製品であるかのみを考慮します。どの工場で生産されていますか。
- ビルダーモードでは、指定された設計図に従って製品を構築する必要があり、スペアパーツを組み立てて新しい製品を生産することが主な目的です。
- 抽象的な工場パターンを、製品ファミリーを生産する自動車部品生産工場とみなすと、ビルダー パターンは、部品を組み立てて完成車を返すことができる自動車組立工場となります。