創造的デザインパターンの紹介

創造的なデザインパターンの概要

1. 前書き

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

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

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

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

Creational Design Patterns are concerned with the way in which objects are created.制御された方法でオブジェクトを作成することにより、複雑さと不安定さを軽減します。

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

Creational Design Patternsは、クライアントを実際の初期化プロセスから完全に切り離すことにより、この問題に対処します。

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

  1. シングルトン–オブジェクトのインスタンスがアプリケーション全体で最大1つのみであることを保証します

  2. ファクトリメソッド-作成する正確なオブジェクトを指定せずに、いくつかの関連クラスのオブジェクトを作成します

  3. 抽象ファクトリー-関連する依存オブジェクトのファミリーを作成します

  4. Builderステップバイステップのアプローチを使用して複雑なオブジェクトを構築します

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

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

シングルトンデザインパターンは、特定のクラスのオブジェクトの初期化をensuring that only one instance of the object exists throughout the Java Virtual Machine.でチェックすることを目的としています。

シングルトンクラスは、オブジェクトへの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;
    }
}

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

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

また、コンストラクターにはprivateアクセス修飾子があることに注意してください。 This is a requirement for creating a Singleton since a public constructor would mean anyone could access it and start creating new instances.

これは元のGoF実装ではないことを忘れないでください。 元のバージョンについては、Javaのシングルトンのthis linked example articleにアクセスしてください。

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

  • 作成に費用がかかるリソース(データベース接続オブジェクトなど)

  • すべてのロガーをシングルトンとして保持することをお勧めします。これにより、パフォーマンスが向上します。

  • アプリケーションの構成設定へのアクセスを提供するクラス

  • 共有モードでアクセスされるリソースを含むクラス

4. 工場メソッドの設計パターン

Factory Design PatternまたはFactory Method Design Patternは、Javaで最も使用されているデザインパターンの1つです。

GoFによると、このパターン“defines an interface for creating an object, but let subclasses decide which class to instantiate. Factoryメソッドを使用すると、クラスはインスタンス化をサブクラスに延期できます。

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

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

4.1. 工場メソッドの設計パターンの例

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

image

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

public interface Polygon {
    String getType();
}

次に、SquareTriangle,などのいくつかの実装を作成します。 このインターフェイスを実装し、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は、関連オブジェクトまたは依存オブジェクトのファミリを作成するために使用されます。 工場の工場と呼ばれることもあります。

詳細な説明については、Abstract Factoryチュートリアルをご覧ください。

6. ビルダーのデザインパターン

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

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

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

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

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

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

この例には、static内部クラスとしてビルダーを含む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メソッドがビルダーオブジェクトを返すことにより、流designな設計アプローチもサポートします。

最後に、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つの異なるタイプ、その利点、例、およびそれらをいつ使用するかについても説明しました。

いつものように、完全なコードスニペットはavailable over on GitHubです。