回顾下设计模式系列《工厂模式》那片文章,里面描述了如何利用工厂模式来模拟一个换灯泡的场景,该场景中模拟了:普通灯泡、节能灯泡、彩色灯泡。它们统一由一个工厂(工厂类)来创造,我们需要使用哪种灯泡时,只需通知工厂类给我们打造一个相同的灯泡即可,类图如下:

      

      由上面边的类图可见,所有类型的灯泡都由灯泡工厂来创建,那这个时候,制造灯泡的工厂因为企业扩大了,需要增加产量,那么此时一个工厂肯定是应付不过来了,而且当前一个工厂所造的灯泡种类也多,更加加大了工厂的制造压力,此时,企业扩建的最好办法就是,增加工厂,各自工厂都只专注于一种灯泡的制造。

     1.工厂方法模式

     首先我们来看看什么是工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类

     工厂方法模式类图如下:

     

      Product:定义工厂方法所创建的对象的接口。相当于上边场景中的灯泡抽象。

      ConcreteProduct:具体实现Product抽象的具体产品类。相当于上边场景中的具体灯泡。

      Creator:声明工厂方法,该方法返回一个Product类型的对象。

      ConcreteCreator:重定义工厂方法返回一个ConcreteProduct实例。

      可能这会大家看完这个类图会有点模糊,不过没关系,接下来我们利用工厂方法模式来实现我们上边的场景(企业扩大需要建立更多的工厂)。

      首先,企业扩大了,需要增加产量建立更多的工厂来创建,为了更好的提高生产效率,每个工厂只专注于生产一种种类的灯泡,利用工厂方法模式设计的类图如下:

     

      边分析边实现代码:

      第一步,工厂扩大了,所造的产品还是灯泡,所以我们还是要把灯泡先抽象出来,为何要抽象请参考《工厂模式》这篇文章。

  1. //灯泡的约定    
  2. public interface IBulb    
  3. {    
  4.     //统一的发光接口    
  5.     public void Luminescence();    
  6. }  

      第二步,还是实现目前所有种类的具体灯泡类型。代码如下:

  1. //灯泡    
  2. public class Bulb : IBulb//实现了灯泡的约定、标准  {        
  3.       //发光       
  4.        public void Luminescence()        
  5.       {           
  6.              //灯泡发光的实现        
  7.        }    
  8. }     
  9. //节能灯泡    
  10. public class FrugalBulb : IBulb //实现了灯泡的约定、标准    
  11. {        
  12.       //节能发光        
  13.       public void Luminescence()        
  14.      {            
  15.         //节能灯泡发光的实现        
  16.       }    
  17. }     
  18. //彩色灯泡    
  19. public class ColorBulb : IBulb    
  20. {        
  21.       //彩色发光        
  22.        public void Luminescence()        
  23.       {            
  24.             //彩色灯泡发光的实现        
  25.        }    
  26. }  
     第三步,因为增加了工厂,但是每个工厂都有一个共同的行为那就是制造灯泡,因此为了将来更方便的增加工厂,我们把工厂也抽象,代码如下:
  1. //工厂的抽象  
  2. public interface IMyBulbFactory  
  3. {  
  4.     IBulb GetBulb();//将来每个工厂都有制造灯泡的行为  

     第四步,实现制造每个种类灯泡的具体工厂,每个工厂都只制造自己所造种类的灯泡,代码如下:

  1. //制造普通灯泡的工厂  
  2.    public class BulbFactory : IMyBulbFactory  
  3.    {  
  4.        //该工厂制造灯泡的行为 只能制造普通灯泡  
  5.        public IBulb GetBulb()  
  6.        {  
  7.            return new Bulb();  
  8.        }  
  9.    }  
  10.  
  11.    //制造节能灯泡的工厂  
  12.    public class FrugalBulbFactroy : IMyBulbFactory  
  13.    {  
  14.        //该工厂制造灯泡的行为 只能制造节能灯泡  
  15.        public IBulb GetBulb()  
  16.        {  
  17.            return new FrugalBulb();  
  18.        }  
  19.    }  
  20.  
  21.    //制造节能灯泡的工厂  
  22.    public class ColorBulbFactroy : IMyBulbFactory  
  23.    {  
  24.        //该工厂制造灯泡的行为 只能制造彩色灯泡  
  25.        public IBulb GetBulb()  
  26.        {  
  27.            return new ColorBulb();  
  28.        }  
  29.    }  
     这样,就满足了企业扩大了,产量提高的问题了,需要什么样的灯泡我们只需要到具体的工厂去获取对应的灯泡就可以啦,主函数调用如下:
  1.   static void Main(string[] args)  
  2.         {  
  3.             //需要普通灯泡  
  4.             IMyBulbFactory bulbFactory = new BulbFactory();  
  5.             IBulb bulb = bulbFactory.GetBulb();  
  6.             bulb.Luminescence(); //普通灯泡发光  
  7.  
  8.             //需要节能灯泡  
  9.             IMyBulbFactory frugalBulbFactroy = new FrugalBulbFactroy();  
  10.             IBulb frugalBulb = frugalBulbFactroy.GetBulb();  
  11.             frugalBulb.Luminescence(); //节能灯泡发光  
  12.  
  13.             //需要彩色灯泡  
  14.             IMyBulbFactory colorbulbFacroty = new ColorBulbFactroy();  
  15.             IBulb colorBulb = colorbulbFacroty.GetBulb();  
  16.             colorBulb.Luminescence(); //彩色灯泡发光  
  17. }  
          那么我们发现,当工厂方法把具体产品的实例化延迟到具体的工厂类时,在客户端调用的时候,我们必须显示的制定使用哪个具体工厂类,这样又加大了耦合度。     

     2.使用反射的工厂方法模式

      那么如何接触上边的耦合度呢?可以通过C#的反射机制,将当前所有工厂都配置到配置文件中,然后在配置一个当前需要使用的工厂节点,配置文件如下:

  1. <?xml version="1.0" encoding="utf-8" ?> 
  2. <configuration> 
  3.     <appSettings> 
  4.         <!--配置所有现有的工厂--> 
  5.         <add key="bulbFactory" value="设计模式系列.工厂方法模式.BulbFactory"></add> 
  6.         <add key="frugalBulbFactory" value="设计模式系列.工厂方法模式.FrugalBulbFactroy"></add> 
  7.         <add key="ColorBulbFactory" value="设计模式系列.工厂方法模式.ColorBulbFactroy"></add> 
  8.  
  9.         <!--配置当前使用的工厂 当前使用普通灯泡的工厂 value="bulbFactory"--> 
  10.         <add key="NonFactory" value="bulbFactory"></add> 
  11.     </appSettings> 
  12. </configuration> 

     这是我们还需要封装一个专门提供具体工厂的类,为了方便这个类我就不考虑耦合了度。代码如下:

  1. //用来提供当前需要使用的工厂 当前使用的工厂在配置文件中nonfactoty节点中配置  
  2.     public class FactroyProvider  
  3.     {  
  4.         public static IMyBulbFactory WhereToFactroy()  
  5.         {  
  6.             string fName = string.Empty;  
  7.             string factoryName = string.Empty;  
  8.             IMyBulbFactory factory = null;  
  9.               
  10.             if(!string.IsNullOrEmpty(ConfigurationManager.AppSettings["NonFactory"]))  
  11.             {  
  12.                 factoryName = ConfigurationManager.AppSettings["NonFactory"];  
  13.                 if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings[factoryName]))  
  14.                 {  
  15.                     fName = ConfigurationManager.AppSettings[factoryName];  
  16.                     factory = Assembly.Load("设计模式系列").CreateInstance(fName) as IMyBulbFactory;  
  17.                 }  
  18.             }  
  19.               
  20.             return factory;  
  21.         }  
  22.     }  
      通过反射找到工厂,我们将来如果又要扩大公司规模只需要增加具体的工厂实例与产品实例,在配置文件中配置一下即可。

     3.工厂方法模式与工厂模式的区别

     说道这里,不知道大家会不会有一个疑问,总之刚开始我是有这个疑问,疑问就是它和工厂模式的区别是什么呢?

     首先我们回顾下《工厂模式》中的工厂类的代码,代码如下:

  1. public class MyBulbFactory    
  2. {                    
  3.      public static IBulb GetBulb(string bulbName)        
  4.      {            
  5.           IBulb bulb = null;            
  6.           //告诉我你要什么灯泡,我制造相应的灯泡给你            
  7.           switch (bulbName)            
  8.           {                
  9.                case "bulb":                    
  10.                bulb = new Bulb();                         
  11.                break;                                
  12.                case "frugalbulb":                    
  13.                bulb = new FrugalBulb();        
  14.                break;                    
  15.                case "colorbulb":    
  16.                   
  17.                 bulb = new ColorBulb();    
  18.                 break;            
  19.            }            
  20.        return bulb;        
  21.      }    
  22. }   
     可见将来我们如果需要新增一种灯泡,需要修改上面的工厂类,在switch中加入一个对应的case分支,这样就违背了开放封闭原则,而工厂方法就是将这些产品类型的实例化延迟到具体的工厂类去实例化,这样将来如果有了新产品就只需要去创建一个新的工厂类和一个具体的产品就可以了,也就是说工厂方法模式完善了简单工厂模式,使它满足了开放封闭原则。但是有个问题,就是客户端每次调用的时候都会需要制定具体实例化哪个工厂类,例如:
  1. //需要普通灯泡  
  2.             IMyBulbFactory bulbFactory = new BulbFactory();//这里就制定了具体的工厂类  
  3.             IBulb bulb = bulbFactory.GetBulb();  
  4.             bulb.Luminescence(); //普通灯泡发光 

 这样就增加了耦合性。我们在上面使用了反射类解除了耦合,但是又一个疑问出来啦,那么简单工厂模式在创建产品类的时候,如果也使用反射的话那么将来在新增产品的时候也就不需要修改工厂类的代码了,也就是说简单工厂模式使用反射一样可以完美的解决开放封闭原则的问题!

      那么新的问题又出来了,那简单工厂模式还有什么作用呢?

      我个人有两种理解的方式,仅代表个人观点,也希望大家都详细的说说自己的观点,不要一句话概括噢~!

      第一种、在早期设计模式应用的时候,编程语言还没有使用反射的机制时,使用工厂方法模式来升级简单工厂模式,使能够支持开放封闭原则。

      第二种、在特殊场景中,工厂方法模式能更好的实现逻辑的组织,例如本篇文章所使用的场景。