1. 序章

ソフトウェアエンジニアリングでは、デザインパターンは、ソフトウェア設計で最も一般的に発生する問題に対する確立されたソリューションを表します。 これは、経験豊富なソフトウェア開発者による試行錯誤を通じて長期間にわたって進化したベストプラクティスを表しています。

デザインパターンは、1994年にErich Gamma、John Vlissides、Ralph Johnson、Richard Helm(Gang of FourまたはGoFとも呼ばれる)によって出版された本 Design Patterns:Elements of Reusable Object-OrientedSoftwareの後に人気を博しました。 。

この記事では、創造的なデザインパターンとそのタイプについて説明します。 また、いくつかのコードサンプルを見て、これらのパターンが設計に適合する状況について説明します。

2. 創造的なデザインパターン

作成デザインパターンは、オブジェクトが作成される方法に関係しています。 制御された方法でオブジェクトを作成することにより、複雑さと不安定さを軽減します。

新しい演算子は、アプリケーション全体にオブジェクトを分散させるため、有害であると見なされることがよくあります。 クラスが緊密に結合されるようになるため、時間の経過とともに実装を変更することが困難になる可能性があります。

作成デザインパターンは、クライアントを実際の初期化プロセスから完全に切り離すことにより、この問題に対処します。

この記事では、4種類のクリエイティブデザインパターンについて説明します。

  1. シングルトン–アプリケーション全体でオブジェクトのインスタンスが最大で1つだけ存在することを保証します
  2. ファクトリメソッド–作成するオブジェクトを正確に指定せずに、いくつかの関連するクラスのオブジェクトを作成します
  3. 抽象ファクトリ–関連する依存オブジェクトのファミリを作成します
  4. Builder 段階的なアプローチを使用して複雑なオブジェクトを構築します

次に、これらの各パターンについて詳しく説明します。

3. シングルトンデザインパターン

シングルトンデザインパターンは、によって特定のクラスのオブジェクトの初期化をチェックし、Java仮想マシン全体にオブジェクトのインスタンスが1つだけ存在するようにすることを目的としています。

シングルトンクラスは、オブジェクトへの1つの一意のグローバルアクセスポイントも提供するため、アクセスポイントへの後続の各呼び出しは、その特定のオブジェクトのみを返します。

3.1. シングルトンパターンの例

シングルトンパターンはGoFによって導入されましたが、元の実装はマルチスレッドシナリオで問題があることが知られています。

したがって、ここでは、静的内部クラスを利用するより最適なアプローチに従います。

public class Singleton  {    
    private Singleton() {}
    
    private static class SingletonHolder {    
        public static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {    
        return SingletonHolder.instance;    
    }
}

ここでは、シングルトンクラスのインスタンスを保持する静的内部クラスを作成しました。 誰かがgetInstance()メソッドを呼び出したときにのみインスタンスを作成し、外部クラスがロードされたときは作成しません。

これは、同期を必要とせず、スレッドセーフであり、遅延初期化を強制し、ボイラープレートが比較的少ないため、シングルトンクラスで広く使用されているアプローチです。

また、コンストラクターには privateaccess修飾子があることに注意してください。 これはシングルトンを作成するための要件です。パブリックコンストラクターは、誰でもシングルトンにアクセスして新しいインスタンスの作成を開始できることを意味するためです。

これは元のGoF実装ではないことを忘れないでください。 元のバージョンについては、Javaのシングルトンに関するこのリンクされたBaeldungの記事にアクセスしてください。

3.2. シングルトンデザインパターンを使用する場合

  • 作成に費用がかかるリソース(データベース接続オブジェクトなど)の場合
  • すべてのロガーをシングルトンとして保持することをお勧めします。これにより、パフォーマンスが向上します。
  • アプリケーションの構成設定へのアクセスを提供するクラス
  • 共有モードでアクセスされるリソースを含むクラス

4. ファクトリメソッドデザインパターン

ファクトリデザインパターンまたはファクトリメソッドデザインパターンは、Javaで最も使用されているデザインパターンの1つです。

GoFによると、このパターンは「オブジェクトを作成するためのインターフェイスを定義しますが、インスタンス化するクラスをサブクラスに決定させます。 Factoryメソッドを使用すると、クラスはサブクラスへのインスタンス化を延期できます」。

このパターンは、仮想コンストラクターのタイプを作成することにより、クライアントから特定のファクトリクラスにクラスを初期化する責任を委任します。

これを実現するために、実際の実装の詳細を隠して、オブジェクトを提供するファクトリに依存しています。 作成されたオブジェクトには、共通のインターフェースを使用してアクセスします。

4.1. ファクトリメソッドデザインパターンの例

この例では、いくつかの具象クラスによって実装されるPolygonインターフェースを作成します。 PolygonFactory は、このファミリからオブジェクトをフェッチするために使用されます。

まず、ポリゴンインターフェイスを作成しましょう。

public interface Polygon {
    String getType();
}

次に、 Square Triangle、などのいくつかの実装を作成します。 このインターフェイスを実装し、Polygonタイプのオブジェクトを返します。

これで、辺の数を引数として取り、このインターフェイスの適切な実装を返すファクトリを作成できます。

public class PolygonFactory {
    public Polygon getPolygon(int numberOfSides) {
        if(numberOfSides == 3) {
            return new Triangle();
        }
        if(numberOfSides == 4) {
            return new Square();
        }
        if(numberOfSides == 5) {
            return new Pentagon();
        }
        if(numberOfSides == 7) {
            return new Heptagon();
        }
        else if(numberOfSides == 8) {
            return new Octagon();
        }
        return null;
    }
}

オブジェクトを直接初期化することなく、クライアントがこのファクトリを信頼して適切なPolygonを提供する方法に注目してください。

4.2. ファクトリメソッドデザインパターンを使用する場合

  • インターフェイスまたは抽象クラスの実装が頻繁に変更されることが予想される場合
  • 現在の実装が新しい変更に快適に対応できない場合
  • 初期化プロセスが比較的単純で、コンストラクターが必要とするパラメーターがほんの一握りの場合

5. 抽象ファクトリデザインパターン

前のセクションでは、ファクトリメソッドデザインパターンを使用して、単一のファミリに関連するオブジェクトを作成する方法を説明しました。

対照的に、Abstract Factory Design Patternは、関連オブジェクトまたは依存オブジェクトのファミリーを作成するために使用されます。 工場の工場と呼ばれることもあります。

詳細な説明については、 AbstractFactoryチュートリアルを確認してください。

6. Builderのデザインパターン

Builderデザインパターンは、比較的複雑なオブジェクトの構築を処理するために設計されたもう1つの作成パターンです。

オブジェクトの作成が複雑になると、Builderパターンは、別のオブジェクト(ビルダー)を使用してオブジェクトを作成することにより、インスタンス化プロセスを分離できます。

次に、このビルダーを使用して、単純なステップバイステップのアプローチを使用して、他の多くの同様の表現を作成できます。

6.1. ビルダーパターンの例

GoFによって導入された元のビルダーデザインパターンは、抽象化に焦点を当てており、複雑なオブジェクトを処理する場合に非常に優れていますが、デザインは少し複雑です。

Joshua Blochは、彼の著書「Effective Java」で、クリーンで読みやすく(流暢なデザインを使用しているため)、クライアントの観点から使いやすいビルダーパターンの改良版を紹介しました。 この例では、そのバージョンについて説明します。

この例には、静的内部クラスとしてビルダーを含むBankAccountという1つのクラスしかありません。

public class BankAccount {
    
    private String name;
    private String accountNumber;
    private String email;
    private boolean newsletter;

    // constructors/getters
    
    public static class BankAccountBuilder {
        // builder code
    }
}

フィールド上のすべてのアクセス修飾子は、外部オブジェクトがそれらに直接アクセスすることを望まないため、privateと宣言されていることに注意してください。

コンストラクターもprivateであるため、このクラスに割り当てられたBuilderのみがコンストラクターにアクセスできます。 コンストラクターで設定されたすべてのプロパティは、引数として提供するビルダーオブジェクトから抽出されます。

static内部クラスでBankAccountBuilderを定義しました。

public static class BankAccountBuilder {
    
    private String name;
    private String accountNumber;
    private String email;
    private boolean newsletter;
    
    public BankAccountBuilder(String name, String accountNumber) {
        this.name = name;
        this.accountNumber = accountNumber;
    }

    public BankAccountBuilder withEmail(String email) {
        this.email = email;
        return this;
    }

    public BankAccountBuilder wantNewsletter(boolean newsletter) {
        this.newsletter = newsletter;
        return this;
    }
    
    public BankAccount build() {
        return new BankAccount(this);
    }
}

外部クラスに含まれているのと同じフィールドのセットを宣言していることに注意してください。 必須フィールドは内部クラスのコンストラクターへの引数として必須ですが、残りのオプションフィールドはsetterメソッドを使用して指定できます。

この実装は、setterメソッドがビルダーオブジェクトを返すようにすることで、流暢な設計アプローチもサポートします。

最後に、buildメソッドは外部クラスのプライベートコンストラクターを呼び出し、それ自体を引数として渡します。 返されたBankAccountは、BankAccountBuilderによって設定されたパラメーターを使用してインスタンス化されます。

動作中のビルダーパターンの簡単な例を見てみましょう。

BankAccount newAccount = new BankAccount
  .BankAccountBuilder("Jon", "22738022275")
  .withEmail("[email protected]")
  .wantNewsletter(true)
  .build();

6.2. Builderパターンを使用する場合

  1. オブジェクトの作成に関連するプロセスが非常に複雑で、必須およびオプションのパラメーターが多数ある場合
  2. コンストラクターパラメーターの数が増えると、コンストラクターのリストが大きくなる場合
  3. クライアントが構築されたオブジェクトに異なる表現を期待する場合

7. 結論

この記事では、Javaでの作成デザインパターンについて学びました。 また、シングルトン、ファクトリメソッド、抽象ファクトリ、ビルダーパターンの4つの異なるタイプ、それらの利点、例、およびそれらをいつ使用する必要があるかについても説明しました。

いつものように、完全なコードスニペットはGitHub利用できます。