抽象工厂模式(Abstract Factory)
意图:提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。
----《设计模式》GOF
动机:
由于需求的变化,往往存在更多系列对象的创建工作。
如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种"封装机制"来避免客户程序和这种"多系列具体对象创建工作"的紧耦合?
应用:用户界面工具包。
模式结构:
心得:
工厂方法把生产产品的方式封装起来了,但是一个工厂只能生产一类对象,当一个工厂需要生产多类产品的时候,就需要使用抽象工厂了。
抽象工厂(AbstractFactory)类定义了一组标准的实现接口,这些接口一般都是和具体的产品类继承层次对应的。如createProductA接口只能生产抽象产品类(AbstratctProductA)的子类产品,因此抽象工厂的具体实现类之间的关系就是个生产了一批不同产品族的组合。这样通过抽象工厂模式可以方便的更换产品族,代码修改的代价只需要更换一个具体的工厂对象就可以了。因此直观上可以把抽象工厂看作一组工厂方法,它的每一个接口都可以提取出一个单独的工厂方法。不过抽象工厂除了反映出这些含义外,还隐含着多类产品之间有种内在的联系,如按钮、菜单、滚动条都是GUI组件。
举例:
对于可以随意更换GUI组件库的需求,抽象工厂就可以完成类似的功能。抽象工厂类(AbstractFactory)封装了GUI组件库必须提供的接口,如创建按钮、菜单、滚动条等。GUI组件类会根据不同的窗口系统而派生具体的组件类,实现抽象工厂的具体类具体工厂类会根据不同的窗口系统选择对应的组件进行生产。这样当更换窗口系统的时候,只需要更换工厂类就能方便的实现运行时窗口系统的更换。
// 工厂基类,包含产生按钮和菜单的接口
internal abstract class Skinkit
{
public abstract Button CreateButton();
public abstract Menu CreateMenu();
}
// 产品按钮基类
internal abstract class Button
{
public abstract void Click();
}
// 产品菜单基类
internal abstract class Menu
{
public abstract void Select();
}
// 实际工厂Windows
internal class WinSkinKit : Skinkit
{
public override Button CreateButton()
{
return new WinButton();
}
public override Menu CreateMenu()
{
return new WinMenu();
}
}
// 实际工厂MacOS
internal class MacSkinKit : Skinkit
{
public override Button CreateButton()
{
return new MacButton();
}
public override Menu CreateMenu()
{
return new MacMenu();
}
}
// 实际产品
internal class WinButton : Button
{
public override void Click()
{
Console.WriteLine("Click Windows Button");
}
}
internal class MacButton : Button
{
public override void Click()
{
Console.WriteLine("Click MacOS Button");
}
}
internal class WinMenu : Menu
{
public override void Select()
{
Console.WriteLine("Select Windows Menu");
}
}
internal class MacMenu : Menu
{
public override void Select()
{
Console.WriteLine("Select MacOS Menu");
}
}
测试函数:
private static void Main(string[] args)
{
Skinkit ws = new WinSkinKit();
Button btn = ws.CreateButton();
btn.Click();
Menu menu = ws.CreateMenu();
menu.Select();
}
显示结果:
Click Windows Button
Select Windows Menu
请按任意键继续. . .
从上面可以看出,需要实例的时候,只需要调用按钮、菜单的基类就行了。无需关心实际工厂,归根结底是抽象基类的应用。