工厂模式但具有对象参数

采用以下经典工厂模式:

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的参数,并且需要此参数来实例化对象?

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

这可能特别适合比萨饼,在那里你通常有相同的shell,只有不同(组合)的浇头。 建筑师非常接近常见的订购方式,例如“萨拉米香肠,西红柿,玉米和双层奶酪”。 OTOH用于“预定义”比萨,您可能需要定义帮助工厂方法,例如createMargaritaPizzacreateHawaiiPizza ,然后在内部使用构建器创建具有特定于该类披萨的浇头的披萨。

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

你可以使用reflection:

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

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

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

为了解决我刚才提到的问题和post中陈述的问题,我建议采用以下方法。

 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 GetMenu(); public abstract IPizza CreatePizza(PizzaDefinition pizzaDefinition); } public class ItalianPizzaFactory : PizzaFactory { public enum PizzaType { HamMushroom, Deluxe, Hawaiian } public override IEnumerable 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,它在内部映射到一些披萨配方表,其中包含完全不同的配方,甚至可以略微更改披萨内容。

你可以尝试这样的事情:

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

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

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

在这种情况下,我可能希望将工厂与“参数对象”结合起来,这将减少传递到静态工厂方法所需的参数数量,这可能使创建逻辑比使用Builder更具可读性和整洁性。 但是,当然也需要创建该参数对象,但至少它应该是整个应用程序中的单一forms。