我知道之前有过类似的问题。在过去的几天里,我一直在阅读很多相关内容,我想我现在可以理解设计和代码流程方面的差异了。困扰我的是,似乎两种模式都可以解决同一组问题而没有真正的理由来选择一种或另一种。 当我试图自己解决这个问题的时候,我试图实现一个小例子(从我在“Head First:Design patterns”一书中找到的那个开始)。 在这个例子中,我试图解决相同的问题两次:一次只使用“工厂方法模式”而另一次使用“抽象工厂模式”。我会告诉你代码然后我会发表一些评论和问题。
public interface IDough { }
public interface ISauce { }
public class NYDough : IDough { }
public class NYSauce : ISauce { }
public class KNDough : IDough { }
public class KNSauce : ISauce { }
// 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();
}
}
如果我使用了模式定义,我会为PizzaStore
选择一个“工厂方法模式”(因为它只构建一种类型的对象,Pizza)和{{1的“抽象工厂模式” 1}}。无论如何,另一个设计原则,你应该“赞成合成而非继承”,这表明我应该总是使用“抽象工厂模式”。
我的问题是:我首先应该选择“工厂方法模式”的原因是什么?
让我们看看第一个实现,即使用Factory方法模式的实现。 Jesse van Assen建议这是一个Template方法模式而不是Factory方法模式。我不相信这是对的。
我们可以将第一个实现分为两部分:第一部分涉及IngredientFactory
,第二部分涉及Pizza
。
1)在第一部分PizzaStore
中,客户依赖于某种具体的面团和酱汁。为了将Pizza与我在Pizza
类中使用的具体对象分离,仅引用接口(Pizza
和IDough
),我让ISauce
的子类决定具体Pizza
和Dough
选择哪个具体。对我来说,这完全符合Factory方法模式的定义:
定义用于创建对象的接口,但让子类决定实例化哪个类。 Factory方法允许类将实例化延迟到子类。
2)在第二部分Sauce
是客户端,它依赖于具体的PizzaStore
。我应用了上面讨论的相同原理。
所以,为了更好地表达(我希望)我真正得到的是为什么说:
Factory Method模式负责创建属于一个系列的产品,而Abstract Factory模式则负责处理多个产品系列。
正如你从我的例子中看到的那样(如果他们是正确的:-))你可以使用这两种模式。
答案 0 :(得分:9)
首先,来自GoF设计模式书的2个引用:
“抽象工厂通常采用工厂方法实现。”
“工厂方法通常由模板方法调用。”
所以这不是在工厂方法和抽象工厂之间进行选择的问题,因为后者可以(并且通常是)由前者实施。
抽象工厂的概念(正如Amir暗示的那样)是将几个总是在一起的具体类的创建分组。在你的例子中,它们应该是NY品种的食物成分而不是KN成分。
但如果你想允许混合和放大匹配(与KN面团和NY souce的披萨有什么不对?)然后抽象工厂不是你的答案。在这种情况下,每个Pizza子类应决定它希望创建哪些具体类。
如果你不想允许这些混合,你应该选择抽象工厂。
答案 1 :(得分:2)
如果你想要一些应该做同样决定的相关工厂方法,那么最好在抽象工厂中进行分组。
我会说你的第一个实现不是工厂方法。工厂方法不是抽象的,它们具有决定根据它们实例化的参数。