通用抽象工厂






4.71/5 (27投票s)
通用抽象工厂设计模式
引言
抽象工厂旨在通过使用接口和类来创建相关或依赖对象族,在具体工厂子类中推迟具体产品的实际创建。如果您不熟悉此设计模式,我建议您阅读 GoF 设计模式一书中的抽象工厂部分。熟悉 C# 泛型的基本原理,特别是约束,也会有帮助。
背景
抽象工厂是一个强大的模式,但也有其局限性。我将首先举例说明每种类型的抽象工厂,然后进行讨论,并指出该模式的一些局限性。接下来的几节内容可能对您来说太基础了,如果是这样,请跳过。之所以进行介绍,是因为该模式的泛型版本的代码实现与非泛型版本相比,在不同类型之间存在一些差异。
相关类型
这是一个创建相关对象族的工厂示例
interface ICarFactory
{
ICarProduct CreateToyota();
ICarProduct CreateHonda();
}
该工厂创建不同品牌的汽车,并且它们彼此完全独立。您可以驾驶一辆丰田车去任何地方,而无需拥有一辆本田。在上面的代码中,这两个函数都返回相同类型的接口。要创建另一个品牌的汽车,您必须修改接口并添加 `ICarProduct CreatePorshe();`,例如。但由于它们都只返回一种特定接口,因此建议使用函数参数来标识要创建的产品类型。这种方法将工厂函数数量最小化到单个 `Create` 函数。这个新函数是另一个称为“工厂方法”的 GoF 模式的骨架,在“抽象工厂”中使用此模式非常常见。
interface ICarFactory
{
ICarProduct Create(int productKind);
}
依赖类型
这是一个创建依赖对象族的工厂示例
interface ICarFactory
{
IEngineProduct CreateEngine();
IFrameProduct CreateFrame();
}
该工厂创建不同类型的汽车产品。每个产品都依赖于其他产品才能创建汽车。没有发动机就不能驾驶汽车。该工厂使用特定的接口来创建产品,这是抽象工厂模式的典型示例。
混合
顾名思义,混合型是这两种模式的混合体。以下是一个示例
interface ICarFactory
{
IEngineProduct CreateEngine(int engineKind);
IFrameProduct CreateFrame(int frameKind);
}
如果需要将产品细分到子类别,请使用混合型。如果您想更花哨,可以在另一个“抽象工厂”中嵌套一个“抽象工厂”,而不是使用“工厂方法”。使用更具体的接口来返回更具体的产品类型提供了更大的灵活性,但仍然不够灵活。除了修改接口代码以创建新产品外,客户端还仅限于使用接口公开的函数。除非客户端进行向下转换以访问接口未公开的其他函数,否则客户端是有限的。在下面的示例中,接口只有一个 `Operate()` 函数,并且使用了向下转换来调用特定于产品的操作 `EngineSpecificOperation()`。
IEngineProduct hybrid = carFactory.CreateEngine(0);
hybrid.Operate();
// Compiler error since this function is not in the interface.
//hybrid.EngineSpecificOperation();
// Downcast to access product-specific operation.
((Hybrid)hybrid).EngineSpecificOperation();
类型转换是一个计算成本高昂的过程,应尽量避免使用。此外,错误的类型转换在编译时不会被识别,会在运行时导致 `InvalidCastException`。在下一节中,我将展示如何改进该模式的灵活性。
Generics
我们如何改进这种模式?通过添加一点泛型!借助泛型,我们可以创建灵活的工厂,支持新产品而无需修改工厂代码,并执行特定于产品的行为而无需进行类型转换。
抽象工厂
下面我们定义一个工厂接口。它只知道一件事,就是构建产品。在这里,工厂可以是任何东西(例如,汽车工厂、飞机工厂等),它也可以生产任何东西。我们还可以让汽车工厂生产汽车工厂通常不会生产的其他对象,例如拖鞋。这没有任何意义,但如果我们愿意,总是可以这样做的。我们也可以限制工厂可以生产的对象。稍后将详细介绍。
interface IFactory<TFactory>
{
TProduct Build<TProduct>() where TProduct : IProduct<TFactory>, new();
}
具体工厂
以下是汽车和飞机工厂的具体实现。在这里,我们通过泛型约束“`where TProduct : IProduct<Car>`”来定义汽车工厂只创建汽车产品。我们还指定了 `new()` 约束,因为我们希望实例化产品。
class Car : IFactory<Car>
{
public TProduct Build<TProduct>() where TProduct : IProduct<Car>, new()
{
Console.WriteLine("Creating Car: " + typeof(TProduct));
return new TProduct();
}
}
class Plane : IFactory<Plane>
{
public TProduct Build<TProduct>() where TProduct : IProduct<Plane>, new()
{
Console.WriteLine("Creating Plane: " + typeof(TProduct));
return new TProduct();
}
}
抽象产品
现在我们有了必要的工厂,我们定义产品接口
interface IProduct<TFactory>
{
void Operate();
}
具体产品
以下是使用泛型抽象工厂“相关”类型的“具体产品”类。根据这些类实现的内容,我们可以知道哪种类型的工厂可以构建这些产品。本田和丰田可以由汽车工厂制造。波音可以由飞机工厂制造。萨博可以由汽车和飞机工厂制造。
class Honda : IProduct<Car>
{
public void Operate()
{
Console.WriteLine("Driving Honda.");
}
public void HondaSpecificOperation()
{
Console.WriteLine("Performing Honda-specific operation.");
}
}
class Toyota : IProduct<Car>
{
public void Operate()
{
Console.WriteLine("Driving Toyota.");
}
public void ToyotaSpecificOperation()
{
Console.WriteLine("Performing Toyota-specific operation.");
}
}
class Boeing : IProduct<Plane>
{
public void Operate()
{
Console.WriteLine("Flying Boeing.");
}
public void BoeingSpecificOperation()
{
Console.WriteLine("Performing Boeing-specific operation.");
}
}
class Saab : IProduct<Car>, IProduct<Plane>
{
public void Operate()
{
Console.WriteLine("Operating Saab.");
}
public void SaabSpecificOperation()
{
Console.WriteLine("Performing Saab-specific operation.");
}
}
以下是使用泛型抽象工厂“依赖”类型的“具体产品”类。与前一种类型一样,我们可以根据这些类实现的内容知道哪种类型的工厂可以制造这些产品。发动机可以由任何工厂制造。框架可以由汽车和飞机工厂制造。方向舵只能由飞机工厂制造。这个实现与其他实现的不同之处在于类的泛型类型参数。此参数允许我们指定我们要构建的实际产品。换句话说,汽车工厂有可能制造飞机框架,反之亦然。当然,这仍然取决于具体工厂约束的配置方式。请继续阅读,稍后您将看到一些示例。
class Engine<TFactory> : IProduct<TFactory>
{
public void Operate()
{
Console.WriteLine("Operating Engine.");
}
public void EngineSpecificOperation()
{
Console.WriteLine("Performing Engine-specific operation.");
}
}
class Frame<TFactory> : IProduct<Car>, IProduct<Plane>
{
public void Operate()
{
Console.WriteLine("Operating Frame.");
}
public void FrameSpecificOperation()
{
Console.WriteLine("Performing Frame-specific operation.");
}
}
class Rudder : IProduct<Plane>
{
public void Operate()
{
Console.WriteLine("Operating Rudder.");
}
public void RudderSpecificOperation()
{
Console.WriteLine("Performing Rudder-specific operation.");
}
}
客户端
现在我们来定义“客户端”。客户端创建抽象工厂的具体实现,然后最终用于创建具体产品。
class Factory<TFactory> where TFactory : IFactory<TFactory>, new()
{
public TProduct Create<TProduct>() where TProduct : IProduct<TFactory>, new()
{
return new TFactory().Build<TProduct>();
}
}
相对客户端调用
以下是“相对”类型的示例客户端调用。
在这里,我们仍然无法访问接口未公开的产品特定操作。在接下来的示例中,我将展示如何实现。
Factory<Car> carFactory = new Factory<Car>();
IProduct<Car> carProduct = carFactory.Create<Toyota>();
carProduct.Operate();
// Not in interface.
//carProduct.ToyotaSpecificOperation();
只是随便玩玩。以下显示我们不能混搭产品,任何尝试都会被编译器捕获。
// Cannot implicitly convert "Honda" to "IProduct<Plane>".
//IProduct<Plane> planeProduct = carFactory.Create<Honda>();
// Cannot implicitly convert "Honda" to "Toyota".
//Toyota toyota = carFactory.Create<Honda>();
Factory<Plane> planeFactory = new Factory<Plane>();
// Cannot implicitly convert type "Boeing" to "ICarProduct".
//IProduct<Car> bad = planeFactory.Create<Boeing>();
// "Boeing" must be convertible to "IProduct<Car>".
//carFactory.Create<Boeing>();
// "Honda" must be convertible to "IProduct<Plane>".
//planeFactory.Create<Honda>();
// "Toyota" must be convertible to "IProduct<Plane>".
//planeFactory.Create<Toyota>();
以下显示我们现在可以访问接口未公开的产品特定操作,而无需进行类型转换。如果这是一个非泛型抽象工厂,我们就必须这样做
Honda honda = (Honda)carFactory.Create(0);
如果创建的内容碰巧不是 `Honda`,我们就会收到 `InvalidCastException`。就在上面,我们看到编译器会捕获不匹配的情况。
Honda honda = carFactory.Create<Honda>();
honda.Operate();
honda.HondaSpecificOperation();
以下显示汽车和飞机工厂都可以创建 `Saab`。
Saab saab1 = carFactory.Create<Saab>();
saab1.Operate();
saab1.SaabSpecificOperation();
Saab saab2 = planeFactory.Create<Saab>();
saab2.Operate();
saab2.SaabSpecificOperation();
依赖客户端调用
以下是一些“依赖”类型的示例客户端调用。
Factory<Car> carFactory = new Factory<Car>();
IProduct<Car> carProduct = carFactory.Create<Engine<Car>>();
carProduct.Operate();
Factory<Plane> planeFactory = new Factory<Plane>();
IProduct<Plane> rudderProduct = planeFactory.Create<Rudder>();
rudderProduct.Operate();
由编译器捕获的示例不匹配项。
// Cannot implicitly convert type "Engine<Car>" to "IProduct<Plane>".
//IProduct<Plane> planeProduct = carFactory.Create<Engine<Car>>();
// Cannot implicitly convert "Engine<Car>" to "Frame<Car>".
//Frame<Car> carFrame = carFactory.Create<Engine<Car>>();
// "Rudder" must be convertible to "IProduct<Car>".
//carFactory.Create<Rudder>();
// "Engine<Plane>" must be convertible to "IProduct<Car>".
//carFactory.Create<Engine<Plane>>();
// Cannot implicitly convert type "Rudder" to "IProduct<Car>".
//IProduct<Car> bad = planeFactory.Create<Rudder>();
// "Engine<Car>" must be convertible to "IProduct<Plane>".
//planeFactory.Create<Engine<Car>>();
访问接口未公开的产品特定操作的示例。
Frame<Car> carFrame = carFactory.Create<Frame<Car>>();
carFrame.Operate();
carFrame.FrameSpecificOperation();
由其他工厂创建的框架的示例。在这里,我们通过泛型类型参数指定我们要构建的实际产品。有些人可能会说有点吓人,如果你想到一架用汽车工厂制造的框架制造的飞机。
carFactory.Create<Frame<Plane>>();
planeFactory.Create<Frame<Car>>();
结论
巨大的灵活性伴随着巨大的复杂性。听起来是不是很明显,我最近看了《蜘蛛侠》?但确实如此,这就是权衡。泛型为抽象工厂模式增加了更大的灵活性,但也增加了复杂性。嗯,如果你仔细想想,复杂性其实并没有那么糟糕。一旦你打下了基础,框架就位了,剩下的就应该很简单了。
历史
- 2007 年 10 月 25 日 - 发表文章
- 2014 年 4 月 22 日 - 文章小幅更新