2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Factoryパターン

Last updated at Posted at 2025-01-02

はじめに

今回はFactoryパターンというデザインパターンを見ていきます.

GoFデザインパターンのオブジェクト生成に関するものとなります.

Factoryパターンとは?

Factoryパターンは,インスタンス生成の責任を専用のクラス(Factory)やメソッドに委ねるパターンとなります.

抽象インタフェースだけを利用して具体的なクラスのインスタンスを生成することを可能にするので,開発途上の具体的なクラスが不安定なときに役立ちます.

このパターンを用いることによって,具体的なクラスに依存するのを避けることができます.
(コードの依存性の削減により,柔軟性が向上します)

Factoryパターンの実装

もともと以下のような構造の作図アプリケーションがあったとします.
スクリーンショット 2025-01-03 015920.png

この図を見ると,Appが具体的なクラス(Square, Circle)のインスタンスを作成しています.

コードにすると以下のようになります.

具体的なクラスの依存
public class App{
    public void run(){
        Shape shape = new Circle();
        shape.draw();
    }
}

この場合,AppクラスがCircleという具体的なクラスを直接指定してインスタンスを生成していることになります.

このような,具体的なクラスに依存してしまっているという問題を解決するためにFactoryパターンを用います.

構造は以下のようになります.
スクリーンショット 2025-01-03 020841.png

それぞれのソースコードは以下のようになります.

  • App.java

ユーザー入力に応じた形状描画ができます.
形状名を入力するたびに適切な形状がファクトリを通じて生成されます.

App.java
import java.util.Scanner;

public class App {
    private final ShapeFactory shapeFactory;

    public App(ShapeFactory shapeFactory) {
        this.shapeFactory = shapeFactory;
    }

    public void run() {
        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.println("Enter the shape to draw (square, circle) or 'exit' to quit:");
            String input = scanner.nextLine();

            if ("exit".equalsIgnoreCase(input)) {
                System.out.println("Exiting application.");
                break;
            }

            try {
                Shape shape = shapeFactory.makeShape(input);
                shape.draw();
            } catch (IllegalArgumentException e) {
                System.out.println(e.getMessage());
            }
        }

        scanner.close();
    }

    public static void main(String[] args) {
        ShapeFactory factory = new ShapeFactoryImplementation();
        App app = new App(factory);
        app.run();
    }
}

  • Shape.java

Shapeのインタフェースとなっており,すべての形状の共通メソッドを定義します.

Shape.java
public interface Shape {
    void draw();
}
  • Square.java

Shapeインタフェースを実装し,Squareの描画方法を定義します.

Square.java
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a square");
    }
}
  • Circle.java

Shapeインタフェースを実装し,Circleの描画方法を定義します.

Circle.java
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}
  • ShapeFactory.java

ファクトリの役割を果たし,形状を生成するメソッドを定義します.
インスタンス生成の責任を果たします.

ShapeFactory.java
public interface ShapeFactory {
    Shape makeSquare();
    Shape makeCircle();
}
  • ShapeFactoryImplementation.java
    ShapeFactoryを実装し,具体的な形状を生成します.
ShapeFactoryImplementation.java
public class ShapeFactoryImplementation implements ShapeFactory {
    @Override
    public Shape makeShape(String shapeName) {
        if (shapeName.equals("Square")) {
            return new Square();
        } else if (shapeName.equals("Circle")) {
            return new Circle();
        }
        throw new IllegalArgumentException("Unknown shape name: " + shapeName);
    }
}

このようにFactoryパターンを使うことで,
Appは具体的なクラスに依存することがなくなりました.

これにより,Appは何の工場かを指定するだけで形状を生成できますし,何の教材が生成されるかを意識する必要もありません.

そして,新たに'Triangle'の図形を追加したいとなったときには,Appを変更することなくソースコードを追加することができます.

Triangleの追加後
// App.java
import java.util.Scanner;

public class App {
    private final ShapeFactory shapeFactory;

    public App(ShapeFactory shapeFactory) {
        this.shapeFactory = shapeFactory;
    }

    public void run() {
        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.println("Enter the shape to draw (square, circle, triangle) or 'exit' to quit:");
            String input = scanner.nextLine();

            if ("exit".equalsIgnoreCase(input)) {
                System.out.println("Exiting application.");
                break;
            }

            try {
                Shape shape = shapeFactory.makeShape(input);
                shape.draw();
            } catch (IllegalArgumentException e) {
                System.out.println(e.getMessage());
            }
        }

        scanner.close();
    }

    public static void main(String[] args) {
        ShapeFactory factory = new ShapeFactoryImplementation();
        App app = new App(factory);
        app.run();
    }
}
// Shape.java
public interface Shape {
    void draw();
}

// Square.java
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a square");
    }
}

// Circle.java
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

// Triangle.java
public class Triangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a triangle");
    }
}

// ShapeFactory.java
public interface ShapeFactory {
    Shape makeShape(String shapeName);
}

// ShapeFactoryImplementation.java
public class ShapeFactoryImplementation implements ShapeFactory {
    @Override
    public Shape makeShape(String shapeName) {
        if ("square".equalsIgnoreCase(shapeName)) {
            return new Square();
        } else if ("circle".equalsIgnoreCase(shapeName)) {
            return new Circle();
        } else if ("triangle".equalsIgnoreCase(shapeName)) {
            return new Triangle();
        }
        throw new IllegalArgumentException("Unknown shape name: " + shapeName);
    }
}

Factoryパターンのデメリット

Factoryパターンの実装などを見てきましたが,もちろんデメリットもあります.

それは,設計の拡張の困難さや,ソースコードを書く量が多くなってしまうことです.

新しいクラスを1つ作るのに,新しいクラスを4つ作らなければならないこともありえます.

その内訳は,新しいクラスを表現するインタフェースとそのFactoryを表現するインタフェース,さらにその2つのインタフェースを実装する具体的なクラスの2つとなります.

クラスが増えればもちろん,その分ソースコードを書く量も多くなります.

Factoryパターンのデメリットはこのような点が考えられます.

最後に

Factoryパターンを見てみましたが,メインのファイルが具体的なクラスに依存することなく
設計をすることの重要性がわかったと思います.

また,抽象クラスの役割などの理解も深めることができました.

参考

アジャイルソフトウェア開発の奥義

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?