抽象工厂主要用来解决多个系列的对象实例问题。
问题的应用场景(来源于李建忠老师的webcast讲座):
如果有一款游戏,里面有"道路,房屋,隧道,丛林"这四类基本设施,而且不同的地图(比如中国区,印度区,欧美区)这些设施的风格各有特色,比如中国区应该是中国风格,印度区可能是古典风格,欧美区可能是现代风格...
并且这些基本设施的风格还有可能会一直不断增加,作为程序员/架构师,您想如何让以后的维护/升级变得更轻松,比如新增加一种风格时,最好只要改改配置,顺便发个新的dll复制到安装目录就完事,而不是把原来的类似
xxx = new ChineseStyleRoad(),全部改成xxx = new NewStyleRoad()...
分析:OO思想中有一个重要原则,即开放封闭原则,展开来讲就是 对修改(也称变化)封闭(也称封装),而对扩展(比如在原来框架上的功能增加)开放。本例场景中的变化点,就于在基础设施风格的变化。
代码:
客户程序依赖的抽象部分
#region client程序依赖的抽象类(这些类之间彼此可能有交互)和抽象工厂,所有系列对象创建过程中的变化封装于此
//道路
public abstract class Road {
public abstract void Show();
}
//房屋
public abstract class Building {
public abstract void Show();
}
//地道
public abstract class Tunnel {
public abstract void Show();
}
//从林
public abstract class Jungle {
public abstract void Show();
}
//客户依赖的抽象工厂
public abstract class FacilitiesFactory
{
public abstract Road CreateRoad();
public abstract Building CreateBuilding();
public abstract Tunnel CreateTunnel();
public abstract Jungle CreateJungle();
}
#endregion
封装好变化点后,客户端程序就可以这样调用了:
客户程序
//客户程序
public class GameManager
{
FacilitiesFactory _facilitiesFactory;
Road _road;
Building _building;
Tunnel _tunnel;
Jungle _jungle;
public GameManager(FacilitiesFactory f)
{
this._facilitiesFactory = f;
}
public void BuildGameFacilities()
{
_road = _facilitiesFactory.CreateRoad();
_building = _facilitiesFactory.CreateBuilding();
_jungle = _facilitiesFactory.CreateJungle();
_tunnel = _facilitiesFactory.CreateTunnel();
}
public void Run()
{
_road.Show();
_building.Show();
_jungle.Show();
_tunnel.Show();
}
}
可以看到,这里并没有任何具体类的依赖,也就是说基本解除了依赖,但不管如何抽象最终还是要得到具体类的实例,下面是具体类和工厂的实现:
具体类和工厂的实现
#region 具体类/工厂的实现(实际开发中,具体的某一系列对象可单独用独立项目发布成独立的程序集,以应对变化,比如增加一个新系统时,仅需要修改配置,再加一个dll就可实现批量替换所有子对象)
#region//现代风格的road,building,tunnel,jungle及对应的工厂
public class ModernRoad : Road
{
public override void Show()
{
Console.WriteLine("现代风格的道路已经建成!");
}
}
public class ModernBuilding : Building
{
public override void Show()
{
Console.WriteLine("现代风格的房屋已经建成!");
}
}
public class ModernTunnel : Tunnel
{
public override void Show()
{
Console.WriteLine("现代风格的隧道已经建成!");
}
}
public class ModernJungle : Jungle
{
public override void Show()
{
Console.WriteLine("现代风格的丛林已经建成!");
}
}
public class ModernFacilityisFactory : FacilitiesFactory
{
public override Road CreateRoad()
{
return new ModernRoad();
}
public override Building CreateBuilding()
{
return new ModernBuilding();
}
public override Tunnel CreateTunnel()
{
return new ModernTunnel();
}
public override Jungle CreateJungle()
{
return new ModernJungle();
}
}
#endregion
#region//古典风格的road,building,tunnel,jungle及对应的工厂
public class ClassicRoad : Road
{
public override void Show()
{
Console.WriteLine("古典风格的道路已经建成!");
}
}
public class ClassicBuilding : Building
{
public override void Show()
{
Console.WriteLine("古典风格的房屋已经建成!");
}
}
public class ClassicTunnel : Tunnel
{
public override void Show()
{
Console.WriteLine("古典风格的隧道已经建成!");
}
}
public class ClassicJungle : Jungle
{
public override void Show()
{
Console.WriteLine("古典风格的丛林已经建成!");
}
}
public class ClassicFacilityisFactory : FacilitiesFactory
{
public override Road CreateRoad()
{
return new ClassicRoad();
}
public override Building CreateBuilding()
{
return new ClassicBuilding();
}
public override Tunnel CreateTunnel()
{
return new ClassicTunnel();
}
public override Jungle CreateJungle()
{
return new ClassicJungle();
}
}
#endregion
#region//中国风格的road,building,tunnel,jungle及对应的工厂
public class ChineseRoad : Road
{
public override void Show()
{
Console.WriteLine("中国风格的道路已经建成!");
}
}
public class ChineseBuilding : Building
{
public override void Show()
{
Console.WriteLine("中国风格的房屋已经建成!");
}
}
public class ChineseTunnel : Tunnel
{
public override void Show()
{
Console.WriteLine("中国风格的隧道已经建成!");
}
}
public class ChineseJungle : Jungle
{
public override void Show()
{
Console.WriteLine("中国风格的丛林已经建成!");
}
}
public class ChineseFacilityisFactory : FacilitiesFactory
{
public override Road CreateRoad()
{
return new ChineseRoad();
}
public override Building CreateBuilding()
{
return new ChineseBuilding();
}
public override Tunnel CreateTunnel()
{
return new ChineseTunnel();
}
public override Jungle CreateJungle()
{
return new ChineseJungle();
}
}
#endregion
#endregion
正式调用:
代码
class Program
{
static void Main(string[] args)
{
//注意下面的写法:
//Assembly.Load("这是里程序集的名称")
//CreateInstance("这是具体类的完整表示法,即:命令空间.类名")
FacilitiesFactory _factory = (FacilitiesFactory)Assembly.Load("AbstractFactory").CreateInstance(ConfigurationManager.AppSettings["FactoryType"].ToString());
GameManager gm = new GameManager(_factory);
gm.BuildGameFacilities();
gm.Run();
Console.Read();
}
}
当然这里还有一个配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="FactoryType" value="AbstractFactory.ClassicFacilityisFactory"/>
</appSettings>
</configuration>
可以看到,利用抽象工厂再配合反射,如果想把所有"中国风格"的设施换成"古典风格"的设施,只需要把配置文件中的<add key="FactoryType" value="AbstractFactory.ChineseFacilityisFactory"/>换成 <add key="FactoryType" value="AbstractFactory.ClassicFacilityisFactory"/>,原代码一行都不用改,是不是很轻松! 如果新增了一种风格,也只要把新增加的"具体类和工厂"所对应的程序集dll添加到程序目录下,然后修改配置文件即可,也不需要修改原来已经稳定运行的程序。