これはデザインパターンシリーズの第二篇 —— ビルダー設計パターンです。私はプッシュする記事がシリーズであることを望んでおり、できるだけ同じ文体を保ち、私の理解を明確に説明するよう努めます。ビルダー設計パターンについては、主に以下のいくつかの側面から学びます。具体的には次のとおりです:
- 概要
- 本質
- 重要な概念
- 具体的な実装
- まとめ
概要#
ビルダー設計パターン(Builder Pattern)は、生成に関するデザインパターンに属し、主に複雑なオブジェクトを作成するために使用されます。複雑なオブジェクトの構築プロセスを抽象化し、異なる実装のビルダーとアセンブラを通じて最終的に異なるオブジェクトを組み立てることができます。異なる実装のビルダーやアセンブラを簡単に追加でき、以前のコードを変更する必要がありません。
本質#
ビルダー設計パターン(Builder Pattern)は、オブジェクトのサブコンポーネントの構築プロセスと組み立てプロセスを分離し、構築と組み立てのデカップリングを実現します。異なるビルダーが同じ組み立て順序を持ち、同じビルダーが異なる組み立て順序を持つことで、異なるオブジェクトを作成できます。これにより、構築と組み立てが十分にデカップリングされ、構築アルゴリズムと組み立てアルゴリズムのデカップリングが実現され、より良い再利用が可能になります。
重要な概念#
-
ビルダー(Builder):異なるサブコンポーネントを構築し、サブコンポーネントを返すか、複雑なオブジェクトを取得するためのメソッドを提供します。構築プロセスをインターフェースまたは抽象クラスとして抽象化し、具体的な異なるビルダーの拡張を容易にします。
-
ディレクター(Director):特定のビルダーを通じて関連するサブコンポーネントを構築し、外部に複雑な製品オブジェクトを構成するためのメソッドを提供します。
複雑なオブジェクトを生成する必要がある場合、特定のディレクターを通じて希望する具体的なオブジェクトを取得できます。組み立てプロセスと構築プロセスについては、ユーザーは気にする必要がなく、具体的なディレクターと具体的なビルダーによって内部で完了します。もちろん、複雑なオブジェクトは多くの属性を持つオブジェクトとして理解できます。
具体的な実装#
以下に、スマートフォンの組み立てプロセスを用いてビルダー設計パターンの具体的な実装を説明します。製品クラスは次のとおりです:
/**
* 製品
* @author jzman
*/
public class Phone {
private Screen screen;
private Camera camera;
private Cpu cpu;
//getter、setter、toString メソッドは省略
//...
}
//サブコンポーネント
class Screen{
private String name;
//...
}
//サブコンポーネント
class Camera{
private String name;
//...
}
//サブコンポーネント
class Cpu{
private String name;
//...
}
抽象ビルダー:
/**
* ビルダー
* @author jzman
*/
public interface PhoneBuilder {
Screen builderScreen();
Camera builderCamera();
Cpu builderCpu();
}
具体的なビルダー:
/**
* 具体的なビルダー
* @author jzman
*/
public class MiPhoneBuilder implements PhoneBuilder{
@Override
public Screen builderScreen() {
System.out.println("画面を構築中...");
return new Screen("Mi-screen");
}
@Override
public Camera builderCamera() {
System.out.println("カメラを構築中...");
return new Camera("Mi-camera");
}
@Override
public Cpu builderCpu() {
System.out.println("CPUを構築中...");
return new Cpu("Mi-cpu");
}
}
抽象ディレクター:
/**
* 抽象ディレクター
* @author jzman
*/
public interface PhoneDirector {
Phone directPhone();
}
具体的なディレクター:
/**
* 具体的なディレクター
* @author jzman
*/
public class MiPhoneDirector implements PhoneDirector{
private PhoneBuilder builder;
public MiPhoneDirector(PhoneBuilder builder) {
this.builder = builder;
}
@Override
public Phone directPhone() {
Phone phone = new Phone();
phone.setScreen(builder.builderScreen());
phone.setCamera(builder.builderCamera());
phone.setCpu(builder.builderCpu());
return phone;
}
}
ユーザーは特定のディレクターを通じて最終製品を直接取得します。具体的には次のとおりです:
public class Client {
public static void main(String[] args) {
PhoneBuilder builder = new MiPhoneBuilder();
MiPhoneDirector director = new MiPhoneDirector(builder);
Phone phone = director.directPhone();
System.out.println(phone);
}
}
実行結果は次のとおりです:
画面を構築中...
カメラを構築中...
CPUを構築中...
Phone [screen=Screen [name=Mi-screen], camera=Camera [name=Mi-camera], cpu=Cpu [name=Mi-cpu]]
最終的に、ユーザーは具体的なディレクター MiPhoneDirector を通じて、明確な組み立て特徴を持つスマートフォンを組み立てます。つまり、画面が Mi-screen 、カメラが Mi-camera 、CPU が Mi-cpu のスマートフォンです。
まとめ#
ビルダー設計パターン(Builder Pattern)は、一般的に共通の特性を持つ複雑なオブジェクトに使用され、そのオブジェクト自体と構築、組み立てプロセスが分離されています。これにより、複雑なオブジェクトの属性を構成可能にすることができます。もちろん、複雑なオブジェクト内部が過度に複雑な場合、より多くのビルダークラスを作成する必要があり、構築プロセスが煩雑になる可能性があります。私は、開発においてビルダー設計パターンを使用する主な目的は、コードをより柔軟に書けるようにするための封装を行うことだと思います。例えば、Picasso や Glide の API にはビルダー設計パターンが使用されており、複雑なオブジェクトのいくつかの属性を構成可能にすることが非常に便利です。