工厂模式但具有对象参数

时间:2010-07-28 14:29:25

标签: c# design-patterns 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.");
        }
    }
}

如果一个(或多个)混凝土比萨饼需要特定于施工中具体实施的参数,该怎么办?例如,假设HamAndMushroom工厂需要一个名为MushroomType的参数,并且需要此参数来实例化对象?

7 个答案:

答案 0 :(得分:19)

您可以将参数添加到工厂的创建者方法中。但是,如果参数的数量越来越多(对我而言,这将超过2-3),特别是如果这些参数中的部分或全部是可选的,并且具有合理的默认值,您可以考虑将工厂变为{{3相反。

这可能特别适合比萨饼,在那里你通常有相同的外壳,只有不同的(组合)浇头。 Builder非常接近于常见的排序方式,例如“萨拉米香肠,西红柿,玉米和双层奶酪的比萨饼”。 OTOH用于“预定义”比萨,您可能想要定义辅助工厂方法,例如createMargaritaPizzacreateHawaiiPizza然后在内部使用构建器创建披萨,其中包含特定于此类披萨的浇头。

答案 1 :(得分:1)

您可以传递一个新参数,例如Map。并查询每个具体构造函数的属性。然后所有方法都具有相同的签名。 但是,使用此解决方案,构造函数的调用者必须知道concret构造函数的特定属性...(耦合)

答案 2 :(得分:0)

您必须为该工厂类添加另一个CreatePizza()方法。这意味着工厂的用户将无法创建这些类型的比萨,除非他们专门使用HamAndMushroomPizzaFactory类的实例。如果他们只是有一个PizzaFactory引用,他们只能调用无参数版本,并且无法一般地创建火腿和蘑菇比萨饼。

答案 3 :(得分:0)

您可以使用反射:

using System.Reflection;

// ...

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

答案 4 :(得分:0)

首先,我觉得抽象类PizzaFactory包含一个抽象的通用方法CreatePizza,它采用更具体的类型{{1}的参数,这似乎很奇怪。 }}

为了解决我刚刚提到的问题以及帖子中提到的问题,我建议采用以下方法。

ItalianPizzaFactory.PizzaType

如您所见,ParseTag()方法可能具有任意复杂性,解析纯文本或加密值。或者Tag字段可以是一个简单的int,它在内部映射到一些披萨配方表,其中包含完全不同的配方,甚至可以略微更改披萨内容。

答案 5 :(得分:0)

您可以尝试这样的事情:

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概念看起来不对。

答案 6 :(得分:0)

当参数计数变得非常高时,我认为工厂变得不那么方便和冗余,因为它的主要观点是使创建过程看不见。

此外,当参数是“必需的”时,我也认为Builder失去了它的魅力。

在这种情况下,我可能希望将工厂与“参数对象”结合起来,这将减少传递给静态工厂方法所需的参数数量,这可能使创建逻辑比使用生成器。但是,当然也需要创建该参数对象,但至少它应该是整个应用程序中的单个形式。