【GoFデザインパターン】Factory Methodパターンを図解で説明

DIPを実現させるFactoryMethodパターン

今回実施していくのは、Factory Methodパターンです。

ぎゅう

Factory?
Laravelでもよく目にするな

Laravelを使ったことがある人は、Factoryというクラスに馴染みがあると思います。

Factoryはインスタンスを生成する処理を持ちます。

こういったFactory系のパターンは3つあり

  • Factoryパターン
  • Factory Methodパターン
  • Abstract Factoryパターン

とあります。

今回紹介するのはFactory Methodパターンです。他の二つは別の機会にやっていきたいと思います。

目次

Factory Methodパターンとは

コンストラクタの代わりに、インスタンスの生成するメソッド(Method)を作るから「FactoryMethod」パターンです。

    public static function FactoryMethod(): Product
    {
        return new Product();
    }

なぜこれがいいのか?

これを理解するのが難しいです。

まず、お伝えしたいこと

まず、お伝えしなければいけないことは、よく目にするFactoryとは異なります。

Factory メソッドなので、インスタンスを生成するメソッドを持つパターンだと思ってください。

クラスというよりもメソッドです。ここが注意です。

そして最終的な目的は、DIP(依存性逆転の原則です。

FactoryMethodパターンは、new ClassName()のインスタンスを生成しないための手法です。

ここを理解しておいてください。

でないと「????」と混乱します。

登場する役割

役割名役割
Creatorインスタンスを生成するメソッド(Factory Method)を持つクラス
ProductFactory Methodによって、インスタンスが生成されるクラス

関係性

FactoryMethodパターン
FactoryMethodパターン
FactoryMethodパターン
FactoryMethodパターン
FactoryMethodパターン
FactoryMethodパターン
FactoryMethodパターン
FactoryMethodパターン

このようにインターフェースという抽象に依存させることで、別の具象クラスに切り替えても変更点は一箇所で完結します。

具体的なコード

上位モジュール

<?php
namespace Business // ビジネスロジック
{
    class Logic
    {
        /**
         * Productを生成する処理
         *
         * @var ICreator
         */
        private ICreator $creator;

        public function __construct(ICreator $creator)
        {
            $this->creator = $creator;
        }

        /**
         * 実行したいロジック
         * Productを生成して、情報を加工する
         *
         * @return IProduct
         */
        public function Do(): IProduct
        {
            $product = $this->creator->FactoryMethod();
            // 処理を実行する
            return $product;
        }
    }

    /**
     * Productを生成するインターフェース
     * FactoryMethodを保持する
     */
    interface ICreator
    {
        /**
         * Productのインスタンスを生成
         *
         * @return IProduct
         */
        public function FactoryMethod(): IProduct;
    }

    /**
     * 生成対象のインターフェース
     */
    interface IProduct { }
}

下位モジュール

<?php
namespace Main
{
    class Program
    {
        /**
         * Undocumented function
         *
         * @param array $args
         * @return void
         */
        public static function Main(array $args): void
        {
            $logic = new Logic(new Creator());
            $logic->do();
        }
    }


    class Creator implements ICreator
    {
        /**
         * Productのインスタンスを生成
         *
         * @return IProduct
         */
        public function FactoryMethod(): IProduct
        {
            return new Product();
        }
    }

    class Product implements IProduct { }
}

依存関係を追う

LogicクラスはICreatorというインターフェースをコンストラクタで取り込む。

つまり、抽象に依存が成立しています。

    class Logic
    {
        /**
         * Productを生成する処理
         *
         * @var ICreator
         */
        private ICreator $creator; // 抽象で依存させる。

        public function __construct(ICreator $creator) // 抽象で依存させる。
        {
            $this->creator = $creator; // DI 依存性の注入
        }

では続いて、ICreatorを見ていきましょう。

この中もIProductというインターフェースを返り値にしています。

つまり、抽象に依存が成立しています。

    /**
     * Productを生成するインターフェース
     * FactoryMethodを保持する
     */
    interface ICreator
    {
        /**
         * Productのインスタンスを生成
         *
         * @return IProduct // 抽象で依存
         */
        public function FactoryMethod(): IProduct; // 対象のインスタンスを取得
    }

ここでようやく取り出したいIProductにたどり着きます。

     /**
     * 生成対象のインターフェース
     */
    interface IProduct { }

この一連の流れはすべて抽象に依存しています。

FactoryMethod()を追う

今回FactoryMethodパターンということもあり、メソッドを追いかけましょう。

    class Logic
    {
        /**
         * Productを生成する処理
         *
         * @var ICreator // 抽象に依存
         */
        private ICreator $creator;

        public function __construct(ICreator $creator)
        {
            $this->creator = $creator; // DI 依存性の注入。抽象に依存
        }

        /**
         * 実行したいロジック
         * Productを生成して、情報を加工する
         *
         * @return IProduct // 抽象に依存
         */
        public function Do(): IProduct // 抽象に依存
        {
            $product = $this->creator->FactoryMethod(); // ICreatorのFactoryMethod()でインスタンスを生成
            // 処理を実行する
            return $product;
        }
    }

このLogicクラスに具象クラスの依存関係がないことがわかるでしょうか?

全部抽象の依存で完結させています

これが達成できるので、インターフェースにインスタンスを生成するFactoryMethod()を定義するのです。

これがFactoryMethodパターンです。

まとめ

インターフェースという抽象で依存することで、具象クラスによる依存を回避できました。

これならまるっと具象クラスを変更しても土台には影響しません。

1箇所だけなら、あまり価値を感じづらいかもしれませんが、10箇所・100箇所と依存箇所が増えれば増えるほど、その価値は高まります。

なぜなら、依存させているのは抽象であるインターフェースなので、

インターフェースと具象クラスの結びつきをしている、たった一箇所を変更するだけで、すべて更新されます。

つまり、変更点が少なくて済みます。

その実現のために、FactoryMethodパターンでインタンスを生成したのです。

やりたかったのは抽象で依存させるという(DIP:依存性逆転の原則)を実現するために、インターフェースでインタンスを生成することでした。Factory Method の利点である「具象クラスを書かずにインスタンスを生成する」という文章を「依存」で置き換えると、「具象クラスに依存せずにインスタンスを生成する」ということになります。

ぎゅう
WEBエンジニア
渋谷でWEBエンジニアとして働く。
LaravelとVue.jsをよく取り扱い、誰でも仕様が伝わるコードを書くことを得意とする。
先輩だろうがプルリクにコメントをして、リファクタしまくる仕様伝わるコード書くマン
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次
閉じる