c# 抽象工厂模式
先来看抽象工厂的大体的结构图
要想明白上面的这幅类图的话,先必须要明确一个概念,
产品族:
在上面的产品列表中呢,有两个产品族,一个是“具体产品A--1”和”具体产品B--1“组成的一个族,
还有一个是“具体产品A--2”和“具体产品B--2”组成的一个族。
产品族就是在不同产品等级结构中,功能相关联的产品组成的家族。
下面就来介绍抽象工厂了(有些内容生涩的话,可以看完 Demo 后回过头来浏览)
下面给出的是抽象工厂的定义:
提供一个创建一系列相关或者是相互依赖对象的接口,而无需指定它们具体的类。
再来看衣服详细的类图
其实从上面这副类图中可以看出,每一个具体的工厂,它都只负责创建一个产品族中的产品的实例,
从抽象工厂中派生出的具体工厂,这些具体工厂产生相同的产品(这些产品都继承自同一父类),
比如,ConcreteFactory1 和 ConcreteFactory2 中的 CreateProductA 这个方法都是产生 AbstractProductA 这种类型的产品,
但是产品的实现却是不同的,比如 ConcreteFactory1 中的 CreateProductA 实现的是产生一个 ConcreteProductA—1 产品,
而 ConcreteFactory2 中的 CreateProductA 实现的是产生一个 ConcreteProductA—2 产品,
总的来说就是不同的具体工厂产生不同的产品族,
而抽象工厂则是定义一个负责创建一组产品(也就是一个产品族)的接口,
比如上面的类图中只存在两个产品族,所以在抽象工厂中便只需要定义两个接口就可以了。
下面来剖析一下抽象工厂的优点和缺点
抽象工厂的最大好处在于交换产品系列非常方便,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,
这样就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置,
比如,我在应用中本来使用的是工厂 ConcreteFactory1 来生成的产品族 1 ,
而现在需求改变了,不再使用产品族 1 ,而必须使用产品族 2 时,
此时,只需要在客户端代码中初始化 ConcreteFactory1 的位置,
把 ConcreteFactory1 改为 ConcreteFactory2 就可以了,这样就成功的将产品族1 切换到了使用产品族2 上面来,
其次,抽象工厂让具体的创建实例过程与客户端分离,客户端是通过他们的抽象接口操纵实例,
产品的具体类名也被具体工厂的实现分类,不会出现在客户端代码中。
这一点上的话,简单工厂和工厂方法也做得很不错,即依赖于抽象。
同时,如果需求需要扩展的话,比如,要重新增加一个产品族,这也很好办,
只需要增加一个具体工厂,然后增加产品族就可以了,
总之是,抽象工厂很好的遵循了开--闭原则和依赖倒置原则。
下面就来看一个 Demo ,从这个 Demo 中看出抽象工厂的优点
先来展现一下具体的类图
上面的类图呢,说明的是有两个具体工厂,一个是 Linux 控件的制造,还有一个是 Windows 控件的制造,
然后,有两个产品族,一个是 WindowsTextBox 和 LinuxTextBox 组成的 TextBox 产品族,
还有一个就是 WindowsButton 和 LinuxButton 组成的 Button 产品族。
下面就来写类了
先来看工厂类吧
namespace AbstractFactory
{
public abstract class AbstractFactory
{
//在抽象工厂中,应该包含所有产品创建的抽象方法
public abstract Button CreateButton();
public abstract TextBox CreateTextBox();
}
}
namespace AbstractFactory
{
public class WindowsFactory:AbstractFactory
{
public override Button CreateButton()
{
return new WindowsButton();
}
public override TextBox CreateTextBox()
{
return new WindowsTextBox();
}
}
}
namespace AbstractFactory
{
public class LinuxFactory:AbstractFactory
{
public override Button CreateButton()
{
return new LinuxButton();
}
public override TextBox CreateTextBox()
{
return new LinuxTextBox();
}
}
}
下面就给出所有的产品类
namespace AbstractFactory
{
public abstract class Button
{
public abstract void DisplayButton();
}
}
using System;
namespace AbstractFactory
{
class LinuxButton:Button
{
public override void DisplayButton()
{
Console.WriteLine("我的类型是:{0}",
this.GetType().ToString());
}
}
}
using System;
namespace AbstractFactory
{
class WindowsButton : Button
{
public override void DisplayButton()
{
Console.WriteLine("我的类型是:{0}",
this.GetType().ToString());
}
}
}
namespace AbstractFactory
{
public abstract class TextBox
{
public abstract void DisplayTextBox();
}
}
using System;
namespace AbstractFactory
{
class LinuxTextBox : TextBox
{
public override void DisplayTextBox()
{
Console.WriteLine("我的类型是:{0}",
this.GetType().ToString());
}
}
}
using System;
namespace AbstractFactory
{
class WindowsTextBox:TextBox
{
public override void DisplayTextBox()
{
Console.WriteLine("我的类型是:{0}",
this.GetType().ToString());
}
}
}
上面就是整个 Demo 的类了,下面就是看一下 Main 函数和效果了
using System;
using AbstractFactory;
namespace AbstractFactoryTest
{
class Program
{
static void Main(string[] args)
{
AbstractFactory.AbstractFactory factory;
Button button;
TextBox textBox;
//Windows 下操作
factory = new WindowsFactory();
button = factory.CreateButton();
textBox = factory.CreateTextBox();
button.DisplayButton();
textBox.DisplayTextBox();
Console.WriteLine();
//Linux 下操作
factory = new LinuxFactory();
button = factory.CreateButton();
textBox = factory.CreateTextBox();
button.DisplayButton();
textBox.DisplayTextBox();
Console.ReadLine();
}
}
}
从上面 Main 函数来看的话,如果你的系统本来是基于 Linux 的话,你只需更改一行代码
factory = new WindowsFactory();
即可实现将系统更改为 Windows ,
抽象工厂在这种情况下是非常有用的,比如,如果要实现后台数据库从 Oracle 转换到 Sql Server,
则采用抽象工厂的思想实现是最好的。
下面总结一下抽象工厂的优缺点
首先,抽象工厂的话,其可以更加方便的实现交换一个产品系列,
就像上面的 Demo 中可以轻易的实现从 Linux 上转换为 Windows,
同时,客户端代码中依赖的是抽象,而非具体的实现,
但是,抽象工厂也是有缺点的,其实这个缺点也很明显,那就是显得过于臃肿,
上面的 Demo 尽管还只有两个产品族,类图就显得有些难看了,
如果产品族一多的话,那么总的类数是成几倍的增加,这样使整个结构变得过于复杂,
类的结构也会变得更为庞大。