简体   繁体   English

工厂模式但具有对象参数

[英]Factory Pattern but with object Parameters

Take the following classic factory pattern: 采用以下经典工厂模式:

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.");
        }
    }
}

What if one (or many) of the Concrete Pizzas requires a parameter specific to the concrete implementation at construction. 如果一个(或多个)混凝土比萨饼需要特定于施工中具体实施的参数,该怎么办? For example, lets say the HamAndMushroom factory requires a parameter called, MushroomType and this parameter would be required to instantiate the object? 例如,假设HamAndMushroom工厂需要一个名为MushroomType的参数,并且需要此参数来实例化对象?

You can add parameters to the creator method(s) of your factory. 您可以将参数添加到工厂的创建者方法中。 However, if the number of parameters is getting higher (for me that would be more than 2-3), and especially if some or all of those parameters are optional with reasonable default values, you may consider turning the factory into a Builder instead. 但是,如果参数的数量越来越多(对于我来说,这将超过2-3),特别是如果这些参数中的部分或全部是可选的,并且具有合理的默认值,您可以考虑将工厂改为生成器

That may be especially appropriate for pizzas, where you usually have the same crust, just with different (combinations) of toppings. 这可能特别适合比萨饼,在那里你通常有相同的外壳,只有不同(组合)的浇头。 A Builder models very closely the common way of ordering eg "a pizza with salami, tomatoes, maize and double cheese". 建筑师非常接近常见的订购方式,例如“萨拉米香肠,西红柿,玉米和双层奶酪”。 OTOH for "predefined" pizzas you may want to define helper factory methods, eg createMargaritaPizza or createHawaiiPizza which then internally use the builder to create a pizza with the toppings specific to that kind of pizza. OTOH用于“预定义”比萨,您可能需要定义辅助工厂方法,例如createMargaritaPizzacreateHawaiiPizza ,然后在内部使用构建器创建具有特定于该类披萨的浇头的披萨。

You could pass a new parameter, such as a Map. 您可以传递一个新参数,例如Map。 And query the properties on each concrete constructor. 并查询每个具体构造函数的属性。 Then all the methods would have the same signature. 然后所有方法都具有相同的签名。 However, with this solution, the caller of the constructor has to know the specific properties of the concret constructor...(Coupling) 但是,使用此解决方案,构造函数的调用者必须知道concret构造函数的特定属性...(耦合)

You would have to add another CreatePizza() method for that factory class. 您必须为该工厂类添加另一个CreatePizza()方法。 And that would mean that users of the factory wouldn't be able to create those types of pizzas unless they were specifically using an instance of the HamAndMushroomPizzaFactory class. 这意味着工厂的用户将无法创建这些类型的比萨,除非他们专门使用HamAndMushroomPizzaFactory类的实例。 If they simply have a PizzaFactory reference, they can only call the parameterless version and won't be able to create ham and mushroom pizzas generically. 如果他们只是有一个PizzaFactory引用,他们只能调用无参数版本,并且无法一般地创建火腿和蘑菇比萨饼。

You can use reflection: 你可以使用反射:

using System.Reflection;

// ...

public override IPizza CreatePizza(PizzaType pizzaType, params object[] parameters) {
            return (IPizza)
                   Activator.CreateInstance(
                        Assembly
                             .GetExecutingAssembly()
                             .GetType(pizzaType.ToString()),
                        parameters);
        }

First of all, it seems strange to me that an abstract class PizzaFactory contains an abstract general method CreatePizza that takes a parameter of a more concrete type ItalianPizzaFactory.PizzaType . 首先,我觉得抽象类PizzaFactory包含一个抽象的通用方法CreatePizza ,它采用更具体的类型ItalianPizzaFactory.PizzaType的参数,这似乎很奇怪。

To cover the problem I have just mentioned and the problem stated in the post, I would suggest the following approach. 为了解决我刚才提到的问题和帖子中陈述的问题,我建议采用以下方法。

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.");
        }
    }
}

As you see, the ParseTag() method may be of arbitrary complexity, parsing a plain text or an encrypted value. 如您所见,ParseTag()方法可能具有任意复杂性,解析纯文本或加密值。 Or the Tag field can be a simple int that is mapped internally to some pizza recipe table, with whole different recipes for even slightly changed pizza content. 或者Tag字段可以是一个简单的int,它在内部映射到一些披萨配方表,其中包含完全不同的配方,甚至可以略微更改披萨内容。

You can try something like this: 你可以尝试这样的事情:

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);
  }
}

IMHO concept of factory with implementation specific parameters looks wrong. 具有实现特定参数的工厂的IMHO概念看起来不对。

When parameter count gets very high, I do think factory becomes less handy and redundant since the main point of it to make the creation process kinf of invisible. 当参数计数变得非常高时,我认为工厂变得不那么方便和多余,因为它的主要观点是使创建过程看不见。

Also, when the parameters are 'required', then I also think Builder loses its charm. 此外,当参数是“必需的”时,我也认为Builder失去了它的魅力。

In this case, I may want to combine factory with a 'Parameter Object' which would reduce the # of parameters needed to be passed into the static factory methods and that could have made the creation logic more readable and neat than using a Builder. 在这种情况下,我可能希望将工厂与“参数对象”结合起来,这将减少传递到静态工厂方法所需的参数数量,这可能使创建逻辑比使用Builder更具可读性和整洁性。 But of course, that parameter object is also needed to be created as well but at least it would be in one, single form across your application. 但是,当然也需要创建该参数对象,但至少它应该是整个应用程序中的一个单一形式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM