Как выбирать между шаблоном фабричного метода и абстрактным фабричным шаблоном

Я знаю, что подобные вопросы задавали и раньше. Я много читал об этом в течение последних двух дней и думаю, что теперь могу понять различия с точки зрения дизайна и потока кода. Что меня беспокоит, так это то, что кажется, что оба шаблона могут решить один и тот же набор проблем без реальной причины выбирать тот или иной. Пока я пытался разобраться в этом сам, я попытался реализовать небольшой пример (начиная с того, что я нашел в книге «Сначала голова: шаблоны проектирования»). В этом примере я дважды пытался решить одну и ту же проблему: один раз с использованием только «шаблона фабричного метода», а другой - с использованием «абстрактного фабричного шаблона». Я покажу вам код, а затем сделаю несколько комментариев и вопрос.

Общие интерфейсы и классы

public interface IDough { }
public interface ISauce { }
public class NYDough : IDough { }
public class NYSauce : ISauce { }
public class KNDough : IDough { }
public class KNSauce : ISauce { }

Чистый шаблон метода Factory

// pure Factory method pattern
public abstract class Pizza
{
    protected IDough Dough { get; set; }
    protected ISauce Sauce { get; set; }
    protected abstract IDough CreateDough();
    protected abstract ISauce CreateSauce();
    public void Prepare()
    {
        Dough = CreateDough();
        Sauce = CreateSauce();
        // do stuff with Dough and Sauce
    }
    public void Bake() { }
    public void Cut() { }
    public void Box() { }
}

public class NYCheesePizza : Pizza
{
    protected override IDough CreateDough()
    {
        return new NYDough();
    }

    protected override ISauce CreateSauce()
    {
        return new NYSauce();
    }
}

public class KNCheesePizza : Pizza
{
    protected override IDough CreateDough()
    {
        return new KNDough();
    }

    protected override ISauce CreateSauce()
    {
        return new KNSauce();
    }

}

public abstract class PizzaStore
{
    public void OrderPizza(string type)
    {
        Pizza pizza = CreatePizza(type);
        pizza.Prepare();
        pizza.Bake();
        pizza.Cut();
        pizza.Box();
    }
    public abstract Pizza CreatePizza(string type);
}

public class NYPizzaStore : PizzaStore
{
    public override Pizza CreatePizza(string type)
    {
        switch (type)
        {
            case "cheese":
                return new NYCheesePizza();
            default:
                return null;
        }
    }
}

public class KNPizzaStore : PizzaStore
{

    public override Pizza CreatePizza(string type)
    {
        switch (type)
        {
            case "cheese":
                return new KNCheesePizza();
            default:
                return null;
        }
    }
}

чистый абстрактный фабричный шаблон

public interface IIngredientFactory
{
    IDough createDough();
    ISauce createSauce();
}

public class NYIngredientFactory : IIngredientFactory
{
    public IDough createDough()
    {
        return new NYDough();
    }

    public ISauce createSauce()
    {
        return new NYSauce();
    }
}

public class KNIngredientFactory : IIngredientFactory
{
    public IDough createDough()
    {
        return new KNDough();
    }

    public ISauce createSauce()
    {
        return new KNSauce();
    }
}

public class Pizza
{
    IDough Dough { get; set; }
    ISauce Sauce { get; set; }
    IIngredientFactory IngredientFactory { get; set; }

    public Pizza(IIngredientFactory ingredientFactory)
    {
        IngredientFactory = ingredientFactory;
    }

    public void Prepare()
    {
        Dough = IngredientFactory.createDough();
        Sauce = IngredientFactory.createSauce();
    }
    public void Bake() { }
    public void Cut() { }
    public void Box() { }
}

public interface IPizzaFactory
{
    Pizza CreatePizza(string type);
}

public class NYPizzaFactory : IPizzaFactory
{
    public Pizza CreatePizza(string type)
    {
        switch (type)
        {
            case "cheese":
                return new Pizza(new NYIngredientFactory());
            default:
                return null;
        }
    }
}

public class KNPizzaFactory : IPizzaFactory
{
    public Pizza CreatePizza(string type)
    {
        switch (type)
        {
            case "cheese":
                return new Pizza(new KNIngredientFactory());
            default:
                return null;
        }
    }
}

public class PizzaStore
{
    IPizzaFactory PizzaFactory { get; set; }

    public PizzaStore(IPizzaFactory pizzaFactory)
    {
        PizzaFactory = pizzaFactory;
    }

    public void OrderPizza(string type)
    {
        Pizza pizza = PizzaFactory.CreatePizza(type);
        pizza.Prepare();
        pizza.Bake();
        pizza.Cut();
        pizza.Box();
    }
}

Если бы я использовал определения шаблонов, я бы выбрал "Factory Method Pattern" для PizzaStore (поскольку он создает только один тип объекта, Пиццу) и «Шаблон абстрактной фабрики» для IngredientFactory . В любом случае, другой принцип дизайна гласит, что вам следует «отдавать предпочтение композиции, а не наследованию», что предполагает, что я всегда должен использовать «абстрактный фабричный шаблон».

Мой вопрос: каковы причины, по которым я должен в первую очередь выбрать «Шаблон фабричного метода»?

РЕДАКТИРОВАТЬ

Давайте взглянем на первую реализацию, ту, которая использует шаблон фабричного метода. Джесси ван Ассен предположил, что это шаблон метода Template вместо шаблона метода Factory. Я не уверен, что это правильно. Мы можем разбить первую реализацию на две части: первая связана с Pizza , а вторая - с PizzaStore .

1) в первой части Пицца - это клиент, который зависит от какого-то конкретного теста и соуса. Чтобы отделить пиццу от конкретных объектов, я использовал в классе Pizza ссылку только на интерфейсы ( IDough и ISauce ), и я разрешил Подклассы Pizza определяют, какой бетон Тесто и Соус выбрать. На мой взгляд, это полностью соответствует определению шаблона метода Factory:

Определите интерфейс для создания объекта, но позвольте подклассам решать, какой класс создать. Метод Factory позволяет классу отложить создание экземпляра до подклассов.

2) во второй части PizzaStore является клиентом и зависит от конкретной пиццы . Я применил те же принципы, о которых говорилось выше.

Итак, чтобы лучше выразить (я надеюсь) то, что я действительно не понимаю, это почему сказано, что:

Шаблон Factory Method отвечает за создание продуктов, принадлежащих к одному семейству, тогда как шаблон Abstract Factory имеет дело с несколькими семействами продуктов.

Как вы видите из моих примеров (при условии, что они верны :-)), с обоими шаблонами можно работать одинаково.

15
задан nawfal 18 April 2013 в 18:29
поделиться