示例代码为了尽可能突显设计模式的特征,采用了极简代码。尽量避免其他代码对理解设计模式产生干扰
定义
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
简而言之,就是抽象工厂模式,可以使具体工厂同时具备创建多种产品的能力。比如接下来的示例代码中,电脑工厂具备生产显示器能力,同时还具备生产主机的能力。
结构导图(引自刘伟老师的博客)
代码
产品代码
产品-显示器
/// <summary>
/// 抽象产品
/// 抽象显示器
/// </summary>
interface IDisplay
{
void Show();
}
/// <summary>
/// 具体产品
/// 苹果显示器
/// </summary>
class AppleDisplay : IDisplay
{
public void Show()
{
Console.WriteLine("苹果显示器正常工作。");
}
}
/// <summary>
/// 具体产品
/// 戴尔显示器
/// </summary>
class DellDisplay : IDisplay
{
public void Show()
{
Console.WriteLine("戴尔显示器正常工作。");
}
}
产品-主机
/// <summary>
/// 抽象产品
/// 抽象主机
/// </summary>
interface IMainframe
{
void Open();
}
/// <summary>
/// 具体产品
/// 苹果主机
/// </summary>
class AppleMainframe : IMainframe
{
public void Open()
{
Console.WriteLine("苹果主机成功开机。");
}
}
/// <summary>
/// 具体产品
/// 戴尔主机
/// </summary>
class DellMainframe : IMainframe
{
public void Open()
{
Console.WriteLine("戴尔主机成功开机。");
}
}
如果使用普通工厂模式,那么每种产品,都应该存在其对应的工厂类,这样就会导致该情况下,要同时提供“显示器工厂类”以及“主机工厂类”,后期添加了键盘,鼠标等产品后,还有再提供更多的对应的工厂类。这无疑会导致项目中的工厂数量过多,一定程度上会增加项目的复杂度。
此时,抽象工厂模式就派上用场了!
工厂代码
/// <summary>
/// 抽象工厂
/// 电脑工厂
/// </summary>
interface IComputerFactory
{
/// <summary>
/// 生产一个显示器
/// </summary>
IDisplay ProduceADisplay();
/// <summary>
/// 生产一个主机
/// </summary>
IMainframe ProduceAMainframe();
}
/// <summary>
/// 具体工厂
/// 苹果电脑工厂
/// </summary>
class AppleComputerFactory : IComputerFactory
{
public IDisplay ProduceADisplay()
{
return new AppleDisplay();
}
public IMainframe ProduceAMainframe()
{
return new AppleMainframe();
}
}
/// <summary>
/// 具体工厂
/// 戴尔电脑工厂
/// </summary>
class DellComputerFactory : IComputerFactory
{
public IDisplay ProduceADisplay()
{
return new DellDisplay();
}
public IMainframe ProduceAMainframe()
{
return new DellMainframe();
}
}
此时,每个电脑厂家,都有其独立的工厂。客户想要一台电脑,只需要找指定厂家定制就足够了。当然,实际上,大家买电脑一般不会直接找厂家,此处只是大概比喻。:P
请看客户端代码
class Program
{
static void Main(string[] args)
{
// 假设此时我想要一台苹果电脑
// 创建苹果电脑工厂
var factory = new AppleComputerFactory();
// 生产出显示器并测试产品是否合格
var display = factory.ProduceADisplay();
display.Show();
// 生产出主机并测试产品是否合格
var mainframe = factory.ProduceAMainframe();
mainframe.Open();
// 完事
Console.WriteLine("电脑生产完成,正在配送···");
}
}
以上。
运行结果
补充讨论
很多人说,后期如果拓展某个具体产品(比如现在只有苹果和戴尔两个品牌的电脑,后期可能添加华为显示器和华为主机),很简单,最多添加个具体工厂足矣。但是如果添加了某种产品(比如现在只有显示器和主机,后期可能添加键盘等外设),那么扩展起来将非常困难,因为想要扩展就需要改动现有抽象工厂。
而一旦对现有抽象工厂进行了改动(比如添加了生产键盘的方法),那么所有继承它的那些具体工厂,都需要同步改动(哪怕有些工厂从始至终也没打算卖键盘,但它一样需要跟着抽象工厂一起被改造),这样算起来,改动并不小。
但其实要我说的话,我不一定非要改动现有的抽象工厂。我可以添加新的抽象工厂。
代码如下:
/// <summary>
/// 抽象工厂
/// 附赠键盘的电脑工厂
/// </summary>
abstract class IComputerFactory_Keyboard : IComputerFactory
{
/// <summary>
/// 持有旧工厂的引用
/// </summary>
protected abstract IComputerFactory OldComputerFactory { get; }
/// <summary>
/// 通过旧工厂生产显示器
/// </summary>
public IDisplay ProduceADisplay()
{
return OldComputerFactory.ProduceADisplay();
}
/// <summary>
/// 生产一个键盘
/// </summary>
public abstract IKeyboard ProduceAKeyboard();
/// <summary>
/// 通过旧工厂生产主机
/// </summary>
public IMainframe ProduceAMainframe()
{
return OldComputerFactory.ProduceAMainframe();
}
}
对应添加一个新的附赠键盘的工厂
/// <summary>
/// 具体工厂
/// 戴尔新工厂-买电脑送键盘
/// </summary>
class DellComputerFactory_Keyboard : IComputerFactory_Keyboard
{
private readonly DellComputerFactory ComputerFactory = new DellComputerFactory();
protected override IComputerFactory OldComputerFactory
{
get
{
return ComputerFactory;
}
}
public override IKeyboard ProduceAKeyboard()
{
return new DellKeyboard();
}
}
如此这般,即可在不改动原工厂的情况下,使指定厂商具备生产键盘的能力了。
PS:C#语言,还可以通过扩展方法的方式,为指定工厂添加一些额外的生产能力。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。