代码改变世界

我的设计模式之旅(3)——抽象工厂模式AbstractFactory

2006-08-02 17:33  努力学习的小熊  阅读(885)  评论(0编辑  收藏  举报

抽象工厂模式,花了一些时间来学习它,它的意图就是“提供一个接口,让该接口负责创建一系列相关或者相互依赖的对象,无需指定它们具体的类”,对于已有系列对象的创建时,根据需要替换起来很方便。但是对于新增系列对象的替换则还是需要像原来一样,最起码要新增加这个我们需要的新类,但是这种良好的结构为我们提供了很方便的扩展方法。个人感觉它的缺点就是不能创建有着不同个数对象的系列,不过应该是可以通过工厂方法来解决的,这个留做以后解决的问题。

看到很多相关文章最后都是付诸于代码的实现,自己感觉TerryLee的就比较好,设计模式设计模式,当然是设计的模式,不是代码的模式。应该是让我们有好的设计,最后代码只是设计的具体表现,让我们的代码看起来更具模块化,相互之间的耦合度更低。

实现设计和实现编码是两个过程,网上写了这么多例子,应该能看出来用来实现一个模式的编码不是一个固定的编码结构,其中可能会有这样那样的变化,但是根据具体项目的需求实现了目的,我认为就是实现了我们的设计。

引一个吕震宇老师文章里很能说明问题的图:
   

我们就是要实现提供一个AbstractFactory接口来实现系列对象的创建,至于对象怎样创建,他们之间体关系是怎样实现的都不需要客户程序来关心,客户程序需要做的就是使用这个接口就得到了它需要的一系列对象。

上周末在和大家讨论的时候,被问道“最后即使是改一处还是需要修改代码啊?而且对于系列产品的创建为什么不写成一个接口?”,第二个问题比较好解答,因为像我前面说的实现不是一个固定的代码结构。第一个问题在我看了TerryLee的文章后得到了解决,两种方案,一种是将这种修改放到配置文件中,实现运行时的维护,另一种就是利用一个反射的机制来实现,在以后进行扩展时扩展者,甚至都不需要知道源代码,只需要提供给扩展者相应的接口即可实现扩展(厚厚~~~这里很佩服TerryLee的这些思考!很受用!)。

这里我也不想自己再写什么实际应用的例子,尽是对自己学习的一个总结吧,将李建忠老师的例子结合TerryLee的方案,将其再修改完善一下而已,仅是自己的一个练习吧。

首先是我们构建的工厂方法的代码,将其贴在下面。

     //道路

     public abstract class Road

     {

     }

     //房屋

     public abstract class Building

     {

     }

     //地道

     public abstract class Tunnel

     {

     }

     //丛林

     public abstract class Jungle

     {

     }

 

     public abstract class FacilitiesFactory

     {

         public abstract Road CreateRoad();

         public abstract Building CreateBuilding();

         public abstract Tunnel CreateTunnel();

         public abstract Jungle CreateJungle();

     }

 

     //现代风格道路

     public class ModernRoad : Road

     {

     }

     //现代风格房屋

     public class ModernBuilding : Building

     {

     }

     //现代风格地道

     public class ModernTunnel : Tunnel

     {

     }

     //现代风格丛林

     public class ModernJungle : Jungle

     {

     }

 

     //现代风格

     public class ModernFacilitiesFactory : 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();

         }

     }

 

     //古典风格道路

     public class ClassicRoad : Road

     {

     }

     //古典风格房屋

     public class ClassicBuilding : Building

     {

     }

     //古典风格地道

     public class ClassicTunnel : Tunnel

     {

     }

     //古典风格丛林

     public class ClassicJungle : Jungle

     {

     }

 

     //古典风格

     public class ClassicFacilitiesFactory : 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();

         }

     }

 

     class GameManager

     {

         FacilitiesFactory facilitiesFactory;

 

         Road road;

         Building building;

         Tunnel tunnel;

         Jungle jungle;

 

         public GameManager(FacilitiesFactory facilitiesFactory)

         {

              this.facilitiesFactory = facilitiesFactory;

         }

 

         public void BuildGameFacilities()

         {

              road = facilitiesFactory.CreateRoad();

 

              building = facilitiesFactory.CreateBuilding();

 

              tunnel = facilitiesFactory.CreateTunnel();

 

              jungle = facilitiesFactory.CreateJungle();

         }

 

         public void Run()

         {

//            road.AAA();

//            building.BBB(road);

//            tunnel.CCC();

//            jungle.DDD(tunnel);

              Console.WriteLine(road);

              Console.WriteLine(building);

              Console.WriteLine(tunnel);

              Console.WriteLine(jungle);

         }

     }

这是李建忠老师的例子,具体的应用客户程序如下。

         public static void Main()

         {

              GameManager g = new GameManager(new ModernFacilitiesFactory());

              g.BuildGameFacilities();

              g.Run();

         }

这里提一下其中GameManager g = new GameManager(new ModernFacilitiesFactory());这句话其实是两句合在一起写的,分开如下:

              FacilitiesFactory f = new ModernFacilitiesFactory();

              GameManager g = new GameManager(f);

TerryLee的第一种改造就是在系列对象不发生系列添加的情况下,使用配置文件来进行例子中场景风格的替换。添加一个App.config文件,在其中加入风格设置的字段。

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

         <appSettings>

              <add key="factoryName" value="ModernFacilitiesFactory"></add>

         </appSettings>

</configuration>

然后,在代码中读取这个配置字段,根据配置字段的值来做实现。首先实现一个构建方法,然后再在客户程序中调用。

         public static FacilitiesFactory GetInstance()

         {

              string factoryName = ConfigurationSettings.AppSettings["factoryName"];

              FacilitiesFactory f;

              switch(factoryName)

              {

                   case "ModernFacilitiesFactory":

                       f = new ModernFacilitiesFactory();

                       break;

                   case "ClassicFacilitiesFactory":

                       f = new ClassicFacilitiesFactory();

                       break;

                   default:

                       f = null;

                       break;

              }

          

              return f;

         }

         //客户程序

         public static void Main()

         {

              GameManager g = new GameManager(GetInstance());

              //GameManager g = new GameManager(new ModernFacilitiesFactory());

              g.BuildGameFacilities();

              g.Run();

         }

其实还有一种需求就是扩展新的系列对象,如果还是不需要对客户程序进行维护,而仅是添加了新的系列对象的类,那将是很舒服的一件事。这样我们就可以通过添加DLL并配合配置文件的使用,就能在不修改源程序代码的情况下,扩展出我们需要的新的系列对象(这里很佩服TerryLee的这种实现)。

         public staticFacilitiesFactory GetInstance()

         {

              string factoryName = ConfigurationSettings.AppSettings["factoryName"];

              FacilitiesFactory f;

              if(factoryName != "")

                   f = (FacilitiesFactory)Assembly.Load(factoryName).CreateInstance(factoryName);

              else

                   f = null;

              return f;

         }

这样,我们在扩展时仅需将扩展的DLL放在相应的路径下并配合配置文件即实现了我们的扩展。