Menu
Coddy logo textTech

ファクトリーパターン

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 66/87。

Factory Pattern(ファクトリーパターン)は、オブジェクトの生成を別のメソッドやクラスに委譲する生成に関するデザインパターンです。コード内で直接 new を使用する代わりに、ファクトリーにオブジェクトの生成を依頼します。これは、共通のインターフェースや親クラスを共有する複数の関連クラスがある場合に特に有用です。

メール、SMS、またはプッシュ通知を送信できる通知システムを考えてみましょう。ファクトリがない場合、コードはすべての具体的なクラスについて知る必要があります。

// ファクトリなし - クライアントはすべての具体的なクラスを知っている必要があります
Notification notification;
if (type.equals("email")) {
    notification = new EmailNotification();
} else if (type.equals("sms")) {
    notification = new SMSNotification();
}

Factoryパターンを使用すると、この生成ロジックを一元化できます。

public interface Notification {
    void send(String message);
}

public class EmailNotification implements Notification {
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}

public class SMSNotification implements Notification {
    public void send(String message) {
        System.out.println("SMS: " + message);
    }
}

public class NotificationFactory {
    public static Notification create(String type) {
        if (type.equals("email")) {
            return new EmailNotification();
        } else if (type.equals("sms")) {
            return new SMSNotification();
        }
        return null;
    }
}

これでクライアントコードはよりクリーンになり、具体的なクラスに依存しなくなりました:

Notification notification = NotificationFactory.create("email");
notification.send("Hello!");  // 出力: Email: Hello!

主な利点は、新しい通知タイプを追加する際にファクトリを更新するだけで済み、クライアントコードは変更されないままになることです。これは「インターフェースに対してプログラミングする」という原則に従っており、システムをより柔軟にし、拡張を容易にします。

challenge icon

チャレンジ

簡単

Factory Pattern(ファクトリパターン)を使用して、図形描画システムを構築しましょう!コードのあちこちで直接図形オブジェクトを作成する代わりに、単純な文字列識別子に基づいてさまざまな種類の図形を生成するファクトリに、作成ロジックを一元化します。

コードは以下の4つのファイルに分けて構成します:

  • Shape.java: すべての図形の共通の規約となる Shape という名前のインターフェースを定義します。このインターフェースには、何も返さず、描画される図形に関する情報を出力する単一のメソッド draw() を宣言する必要があります。
  • Shapes.java: Shape インターフェースを実装する3つのクラスを作成します:

    Circle - その draw() メソッドは "Drawing a Circle" と出力する必要があります。

    Rectangle - その draw() メソッドは "Drawing a Rectangle" と出力する必要があります。

    Triangle - その draw() メソッドは "Drawing a Triangle" と出力する必要があります。

  • ShapeFactory.java: 図形の作成を処理するファクトリクラスを作成します。ShapeFactory には、Shape を返す static メソッド createShape(String type) を含める必要があります。type パラメータに基づいて以下のように処理します:
    • type が "circle" と等しい場合、新しい Circle を返します。
    • type が "rectangle" と等しい場合、新しい Rectangle を返します。
    • type が "triangle" と等しい場合、新しい Triangle を返します。
    • それ以外の値の場合は、null を返します。
  • Main.java: ファクトリシステムを統合しましょう!2つの入力(どちらも String 型の図形タイプ)を受け取ります。

    各入力に対して、ShapeFactory を使用して図形を作成し、それを Shape 変数に格納します。ファクトリが有効な図形(null ではない)を返した場合は、その draw() メソッドを呼び出します。null を返した場合は、"Unknown shape: [type]" と出力します([type] は提供された入力値です)。

    両方の入力を順番に処理し、それぞれを独自の出力行に表示してください。

入力として、1つ目の図形タイプ(String)と2つ目の図形タイプ(String)を順番に受け取ります。

Main クラスが new Circle()new Rectangle() を直接使用していないことに注目してください。Main クラスは Shape インターフェースのことしか知らず、オブジェクトの作成はファクトリに依頼しています。これが Factory Pattern の威力です。クライアントコードが具体的なクラスから切り離されているのです!

チートシート

Factory Pattern(ファクトリパターン)は、オブジェクトの生成を別のメソッドやクラスに委譲する生成に関するデザインパターンです。これにより、生成ロジックを集中させ、クライアントコードを具体的な実装から切り離すことができます。

new を使って直接オブジェクトをインスタンス化する代わりに、ファクトリメソッドを使用します:

// Factoryなし - クライアントは具体的なクラスに依存します
Notification notification;
if (type.equals("email")) {
    notification = new EmailNotification();
} else if (type.equals("sms")) {
    notification = new SMSNotification();
}

Factory Patternを使用する場合、共通のインターフェースと具体的な実装を定義します:

public interface Notification {
    void send(String message);
}

public class EmailNotification implements Notification {
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}

public class SMSNotification implements Notification {
    public void send(String message) {
        System.out.println("SMS: " + message);
    }
}

静的な生成メソッドを持つファクトリクラスを作成します:

public class NotificationFactory {
    public static Notification create(String type) {
        if (type.equals("email")) {
            return new EmailNotification();
        } else if (type.equals("sms")) {
            return new SMSNotification();
        }
        return null;
    }
}

クライアントコードは、直接インスタンス化する代わりにファクトリを使用します:

Notification notification = NotificationFactory.create("email");
notification.send("Hello!");  // 出力: Email: Hello!

利点: 新しいタイプを追加する場合でも、ファクトリを更新するだけで済み、クライアントコードは変更されません。これはインターフェースに対してプログラミングするという原則に従っており、システムをより柔軟にし、拡張を容易にします。

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 2つの図形の種類を読み込む
        String type1 = scanner.nextLine();
        String type2 = scanner.nextLine();
        
        // TODO: ShapeFactory を使用して最初の図形を作成する
        // 図形が null でない場合、draw() を呼び出す
        // 図形が null の場合、"Unknown shape: [type]" と出力する
        
        // TODO: 2番目の図形に対しても同様の処理を行う
        
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン