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

2022年10月12日

ファクトリー設計パターンの実現テストします

シナリオ

ピザショップの例で勉強しましょう

ニューヨークとシカゴに支店を持つピザチェーン店の運用アプリを作ろうと思います
ピザが数種類とトッピングが数種類あります

支店種類
ニューヨーク
ニューヨークスタイルチーズピザ
ニューヨークスタイルギリシャピザ
ニューヨークスタイルペッパーピザ
シカゴ
シカゴスタイルチーズピザ
シカゴスタイルギリシャピザ
シカゴスタイルペッパーピザ

クラス図

店舗マネージャコード

namespace P112Factory
{
    public class StoreSystemManager
    {
        private static void Main(string[] args)
        {
            PizzaStore nyStore = new NYPizzaStore();
            Pizza? pizza = nyStore.OrderPizza("チーズ");
            Console.WriteLine($"イーサンの注文は、{pizza?.Name}");

            Console.WriteLine();

            PizzaStore chikagoStore = new ChicogoPizzaStore();
            pizza = chikagoStore.OrderPizza("チーズ");
            Console.WriteLine($"ジョエルの注文は、{pizza?.Name}");
        }
    }
}

説明

支店を作ります

PizzaStore nyStore = new NYPizzaStore();

支店でピザを注文します

Pizza? pizza = nyStore.OrderPizza("チーズ");

注文したピザ名を表示します

Console.WriteLine($"イーサンの注文は、{pizza?.Name}");

支店ごとのコード

using P112Factory.Factory;

namespace P112Factory
{
    internal class NYPizzaStore : PizzaStore
    {
        protected override Pizza? CreatePizza(string type)
        {
            Pizza? pizza = null;

            IPizzaIngredientFactory ingredientFactory = new NYPizzaInredientFactory();

            if (type == "チーズ")
            {
                pizza = new CheesePizza(ingredientFactory);
                pizza.Name = "ニューヨークスタイルチーズピザ";
                return pizza;
            }

            if (type == "野菜")
            {
                pizza = new VeggiesPizza(ingredientFactory);
                pizza.Name = "ニューヨークスタイル野菜ピザ";
                return pizza;
            }

            if (type == "ペパロニ")
            {
                pizza = new PepperoniPizza(ingredientFactory);
                pizza.Name = "ニューヨークスタイルペパロニピザ";
                return pizza;
            }

            return pizza;
        }
    }
}

説明

CreatePizzaメソッドは、ピザを作るためのメソッドになります
作るピザの種類を引数で渡します
戻り値は作ったピザのインスタンスです

protected override Pizza? CreatePizza(string type)

引数はどの食材工場を使うかを指定します
今回のサンプルだとニューヨークの工場になりますね

if (type == "チーズ")
{
    pizza = new CheesePizza(ingredientFactory);
    pizza.Name = "ニューヨークスタイルチーズピザ";
    return pizza;
}

ピザ食材工場のインターフェースコード

using System;
namespace P112Factory
{
    /// <summary>
    /// ピザ食材工場
    /// 各食材用の作成メソッドを定義します
    /// </summary>
    public interface IPizzaIngredientFactory
    {
        Dough CreateDough();
        Sause CreateSauce();
        Cheese CreateCheese();
        List<Veggies> CreateVeggies();
        Pepperoni CreatePepperoni();
        Clams CreateClam();
    }
}

説明

各工場が備えなければならないメソッドを定義します

Dough CreateDough();

サンプルでは、Dough(生地)を作る場合、工場ごとに必要なメソッドを作成することになります

各食材工場のコード

using System;
namespace P112Factory.Factory
{
    /// <summary>
    /// ニューヨーク食材工場
    /// </summary>
    public class NYPizzaInredientFactory : IPizzaIngredientFactory
    {
        public NYPizzaInredientFactory()
        {
        }

        public Cheese CreateCheese()
        {
            return new ReggianoCheese();
        }

        public Clams CreateClam()
        {
            return new FreshClams();
        }

        public Dough CreateDough()
        {
            return new ThinCrustDough();
        }

        public Pepperoni CreatePepperoni()
        {
            return new SlicedPepperoni();
        }

        public Sause CreateSauce()
        {
            return new MarinaraSauce();
        }

        public List<Veggies> CreateVeggies()
        {
            List<Veggies> veggies = new List<Veggies>() { new Garlic(), new Onion(), new Mushroom(), new RedPper() };
            return veggies;
        }
    }
}

説明

各工場の具象(実体)になります

public Dough CreateDough()
{
    return new ThinCrustDough();
}

サンプルでは、Dough(生地)を作る場合、この工場での生地(薄皮の生地)を作成して返します

ピザの種類ごとのコード

コンストラクタにどの食材工場を使うかを指定します
新しくインスタンスが作られた時の値になります
ピザ基本クラスの派生クラスにあたります
食材は、工場ごとに変わります

using System;
namespace P112Factory
{
    internal class CheesePizza : Pizza
    {
        IPizzaIngredientFactory ingredientFactory;

        public CheesePizza()
        {
        }

        public CheesePizza(IPizzaIngredientFactory ingredientFactory)
        {
            this.ingredientFactory = ingredientFactory;
        }

        public override void Prepare()
        {
            Console.WriteLine($"{Name}を下準備");
            Dough = ingredientFactory.CreateDough();
            Sauce = ingredientFactory.CreateSauce();
            Cheese = ingredientFactory.CreateCheese();
        }
    }
}

ピザの基本クラスコード

namespace P112Factory
{
    abstract class Pizza
    {
        /// <summary>
        /// 名前
        /// </summary>
        public string? Name { get; set; }
        /// <summary>
        /// 生地
        /// </summary>
        public Dough? Dough { get; set; }
        /// <summary>
        /// ソース
        /// </summary>
        public Sause? Sauce { get; set; }
        /// <summary>
        /// 野菜
        /// /// </summary>
        public List<Veggies> Veggies { get; set; } = new List<Veggies>();
        /// <summary>
        /// チーズ
        /// </summary>
        public Cheese? Cheese { get; set; }
        /// <summary>
        /// ペッパローニ
        /// </summary>
        public Pepperoni? Pepperoni { get; set; }
        /// <summary>
        /// あさり
        /// </summary>
        public Clams? Clams { get; set; }

        /// <summary>
        /// トッピングリスト
        /// </summary>
        public List<string> Toppings { get; set; } = new List<string>();

        /// <summary>
        /// 準備する
        /// </summary>
        abstract public void Prepare();

        public void Bake()
        {
            Console.WriteLine("180度で25 分焼く");
        }

        public virtual void Cut()
        {
            Console.WriteLine("ピザを扇型にカットする");
        }

        public void Box()
        {
            Console.WriteLine("PizaaStoreの箱にピザを入れる");
        }
    }
}

説明

野菜のトッピングごとのトッピングをリストしておくプロパティになります

public List<Veggies> Veggies { get; set; } = new List<Veggies>();

Prepare(作る)作業のの詳細を記したメソッドになります

public void Prepare()

結果

ニューヨークスタイルチーズピザを下準備
180度で25 分焼く
ピザを扇型にカットする
PizaaStoreの箱にピザを入れる
イーサンの注文は、ニューヨークスタイルチーズピザ

シカゴスタイルチーズピザを下準備
180度で25 分焼く
ピザを扇型にカットする
PizaaStoreの箱にピザを入れる
ジョエルの注文は、シカゴスタイルチーズピザ

参考