Заводская модель, но с параметрами объекта - PullRequest
21 голосов
/ 28 июля 2010

Возьмите следующий классический фабричный шаблон:

public interface IPizza
{
    decimal Price { get; }
}

public class HamAndMushroomPizza : IPizza
{
    decimal IPizza.Price
    {
        get
        {
            return 8.5m;
        }
    }
}
public abstract class PizzaFactory
{
    public abstract IPizza CreatePizza(ItalianPizzaFactory.PizzaType pizzaType);
}

public class ItalianPizzaFactory : PizzaFactory
{
    public enum PizzaType
    {
        HamMushroom,
        Deluxe,
        Hawaiian
    }

    public override IPizza CreatePizza(PizzaType pizzaType)
    {
        switch (pizzaType)
        {
            case PizzaType.HamMushroom:
                return new HamAndMushroomPizza();
            case PizzaType.Hawaiian:
                return new HawaiianPizza();
            default:
                throw new ArgumentException("The pizza type " + pizzaType + " is not recognized.");
        }
    }
}

Что делать, если для одной (или многих) из конкретной пиццы требуется параметр, специфичный для конкретной реализации при строительстве. Например, допустим, фабрике HamAndMushroom требуется параметр с именем MushroomType, и этот параметр потребуется для создания экземпляра объекта?

Ответы [ 7 ]

19 голосов
/ 28 июля 2010

Вы можете добавить параметры в метод (ы) создателя вашей фабрики. Однако, если число параметров становится больше (для меня это будет больше чем 2-3), и особенно если некоторые или все эти параметры являются необязательными с приемлемыми значениями по умолчанию, вы можете рассмотреть возможность превращения фабрики в Строитель вместо.

Это может быть особенно подходящим для пиццы, где у вас обычно одна и та же корочка, только с разными (комбинациями) начинок. Строитель очень близко моделирует общий способ заказа, например "пицца с салями, помидорами, кукурузой и двойным сыром". OTOH для «предопределенных» пицц вы можете определить вспомогательные фабричные методы, например, createMargaritaPizza или createHawaiiPizza, которые затем внутренне используют застройщика для создания пиццы с начинками, характерными для этого вида пиццы.

1 голос
/ 28 июля 2010

Вы можете передать новый параметр, такой как Карта.И запросить свойства каждого конкретного конструктора.Тогда все методы будут иметь одинаковую подпись.Тем не менее, с этим решением, вызывающая сторона конструктора должна знать конкретные свойства конструктора бетонной конструкции ... (Соединение)

0 голосов
/ 28 ноября 2017

Когда количество параметров становится очень большим, я думаю, что фабрика становится менее удобной и избыточной, поскольку главная цель - сделать процесс создания невидимым.

Кроме того, когда параметры «обязательны», я также думаю, что Builder теряет свою прелесть.

В этом случае я могу захотеть объединить фабрику с «Объектом параметров», который уменьшил бы число параметров, которые необходимо передать в статические фабричные методы, и мог бы сделать логику создания более читабельной и аккуратной, чем использование Builder. Но, разумеется, этот объект параметра также необходимо создать, но по крайней мере он будет в одной единой форме в вашем приложении.

0 голосов
/ 28 июля 2010

Вы можете попробовать что-то вроде этого:

interface IPizza
{
}

class Pizza1 : IPizza
{
  public Pizza1(Pizza1Parameter p)
  {
  }
}

class Pizza2 : IPizza
{
  public Pizza2(Pizza2Parameter p)
  {
  }
}

interface IPizzaParameter
{
  object Type { get; set; }
}

class Pizza1Parameter : IPizzaParameter
{
  public object Type { get; set; }
}

class Pizza2Parameter : IPizzaParameter
{
  public object Type { get; set; }
}

static class PizzaFactory
{
  public enum PizzaType
  {
    Pizza1,
    Pizza2,
  }

  public static IPizza CreatePizza(PizzaType type, IPizzaParameter param)
  {
    switch (type)
    {
      case PizzaType.Pizza1:
        return new Pizza1(param as Pizza1Parameter);
      case PizzaType.Pizza2:
        return new Pizza2(param as Pizza2Parameter);
    }

    throw new ArgumentException();
  }
}

class Program
{
  static void Main()
  {
    var param1 = new Pizza1Parameter();
    var p1 = PizzaFactory.CreatePizza(PizzaFactory.PizzaType.Pizza1, param1);
  }
}

ИМХО концепция фабрики с конкретными параметрами реализации выглядит неправильно.

0 голосов
/ 28 июля 2010

Прежде всего, мне кажется странным, что абстрактный класс PizzaFactory содержит абстрактный общий метод CreatePizza, который принимает параметр более конкретного типа ItalianPizzaFactory.PizzaType.

Чтобы охватить проблему, которую я только что упомянул, и проблему, изложенную в посте, я бы предложил следующий подход.

public struct PizzaDefinition
{
    public readonly string Tag; 
    public readonly string Name;
    public readonly string Description;
    public PizzaDefinition(string tag, string name, string description)
    {
        Tag = tag; Name = name; Description = description;
    }
}

public abstract class PizzaFactory
{
    public abstract IEnumerable<PizzaDefinition> GetMenu();
    public abstract IPizza CreatePizza(PizzaDefinition pizzaDefinition);
}


public class ItalianPizzaFactory : PizzaFactory
{
    public enum PizzaType
    {
        HamMushroom,
        Deluxe,
        Hawaiian
    }    

    public override IEnumerable<PizzaDefinition> GetMenu()
    {
        return new PizzaDefinition[] {
            new PizzaDefinition("hm:mushroom1,cheese3", "Ham&Mushroom 1", "blabla"),
            new PizzaDefinition("hm:mushroom2,cheese1", "Ham&Mushroom 2", "blabla"),
            new PizzaDefinition("dx", "Deluxe", "blabla"),
            new PizzaDefinition("Hawaian:shrimps,caramel", "Hawaian", "blabla")
        };
    }

    private PizzaType ParseTag(string tag, out object[] options){...}

    public override IPizza CreatePizza(PizzaDefinition pizzaDefinition)
    {
        object[] options;
        switch (ParseTag(pizzaDefinition.Tag, out options))
        {
            case PizzaType.HamMushroom:
                return new HamAndMushroomPizza(options);
            case PizzaType.Hawaiian:
                return new HawaiianPizza();
            default:
                throw new ArgumentException("The pizza" + pizzaDefinition.Name + " is not on the menu.");
        }
    }
}

Как видите, метод ParseTag () может иметь произвольную сложность, анализируя простой текст или зашифрованное значение. Или поле Tag может быть простым int, которое отображается внутри некоторой таблицы рецептов пиццы с совершенно разными рецептами даже для слегка измененного содержания пиццы.

0 голосов
/ 28 июля 2010

Вы можете использовать отражение:

using System.Reflection;

// ...

public override IPizza CreatePizza(PizzaType pizzaType, params object[] parameters) {
            return (IPizza)
                   Activator.CreateInstance(
                        Assembly
                             .GetExecutingAssembly()
                             .GetType(pizzaType.ToString()),
                        parameters);
        }
0 голосов
/ 28 июля 2010

Вы должны добавить еще один метод CreatePizza () для этого класса фабрики. И это будет означать, что пользователи фабрики не смогут создавать пиццы такого типа, если они не будут использовать экземпляр класса HamAndMushroomPizzaFactory. Если у них просто есть ссылка на PizzaFactory, они могут вызвать только версию без параметров и не смогут создавать пиццу с ветчиной и грибами вообще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...