工厂模式在当初学习.net时就听说过,记得当初实习的简历上写着熟悉三层架构和工厂模式,其实当初并不会,只是听说过。直到前些时候才去了解工厂模式,其实工厂模式还是很常用的,参考大话设计模式和网上的若干博客,初步整理出四种版本的,简单工厂模式、工厂模式、改进的反射工厂模式、抽象工厂模式。
首先先大概了解下实际运用背景,比如要设计一个集成平台将停车场集成进来,而停车场有多个品牌供用户选择,这种情况就需要运用工厂模式。将停车场的特征方法提取出来,每新加一种停车场时实现该方法即可,客户端只需少许更改即可实现动态选择产品。
首先不使用工厂模式实现:
public class ParkA { public void GetParkInfo() { Console.WriteLine("ParkA"); } } public class ParkB { public void GetParkInfo() { Console.WriteLine("ParkB"); } }
客户端:
class Program { static void Main(string[] args) { ParkA park = new ParkA(); park.GetParkInfo(); Console.ReadLine(); } }
这客户端是用ParkA,又一客户用ParkB,把这段代码去掉换成ParkB交给客户,第三个客户用ParkA,只能再改回ParkA,第一个客户和第三个客户明明一样,但也没法直接用(因为第二个客户时已经改成ParkB了),如此很多时侯就是在做重复劳动,而且每卖一个都要开发人员根据其品牌修改一番。
1.简单工厂
简单工厂上场,面向接口编程,将选择品牌的过程交给客户,将停车场提取出接口,每个品牌都实现这个接口,然后提供一个工厂,用户只要选择自己的品牌即可。
public interface IPark { void GetParkInfo(); } public class ParkA : IPark { public void GetParkInfo() { Console.WriteLine("ParkA"); } } public class ParkB : IPark { public void GetParkInfo() { Console.WriteLine("ParkB"); } } public enum ParkTypeEnum { ParkA, ParkB } public class ParkFactory { public static IPark CreatePark(ParkTypeEnum parkType) { switch (parkType) { case ParkTypeEnum.ParkA: return new ParkA(); case ParkTypeEnum.ParkB: return new ParkB(); default: return null; } } }
客户端:
class Program { static void Main(string[] args) { IPark park = ParkFactorys.ParkFactory.CreatePark(ParkTypeEnum.ParkA); park.GetParkInfo(); Console.ReadLine(); } }
当有新品牌时只需实现接口,修改工厂即可。而对于已有的品牌则无需修改。
这里有个理论依据,里氏替换法则:任何接收父类型的地方,都应当能够接收子类型,换句话说如果使用的是一个基类的话,那么一定适用于其子类,而且程序察觉不出基类对象和子类对象的区别。
开放封闭原则:对扩展是开放的,而对修改是封闭的。
缺点:违反了开放封闭原则,对修改开放了,在增加一个新的品牌时必须相应增加工厂的switch分支。
2.工厂模式
相对简单工厂变化之处,就是工厂发生了变化,将工厂也抽象出来一个工厂接口,里面只有一个方法,用来创建实例。每个品牌都有自己的工厂,各自实现工厂接口即可。
public interface IPark { void GetParkInfo(); } public class ParkA : IPark { public void GetParkInfo() { Console.WriteLine("ParkA"); } } public class ParkB : IPark { public void GetParkInfo() { Console.WriteLine("ParkB"); } } public interface IFactoryPark { IPark CreatePark(); } public class ParkFactoryA : IFactoryPark { public IPark CreatePark() { return new ParkA(); } } public class ParkFactoryB : IFactoryPark { public IPark CreatePark() { return new ParkB(); } }
客户端:
class Program { static void Main(string[] args) { IFactoryPark factory = new ParkFactoryA(); IPark park=factory.CreatePark(); park.GetParkInfo(); Console.ReadLine(); } }
这样就符合了开放封闭原则,新加品牌同时新加相应的工厂,而无需修改原有的工厂。
缺点:品牌多了之后工厂也会随之变得很多。
3.反射工厂模式
针对简单工厂模式,还有一种改进,利用反射来封闭修改。修改工厂类,如下:
public class ParkFactory { public static IPark CreatePark(string typeName) { #region 方法一 //load中是程序集的名称,也就是生成的DLL的名称 //CreateInstance后面的是类的完整名称,带命名空间 //IPark park = Assembly.Load("ParkFactorys").CreateInstance(typeName) as IPark; #endregion #region 方法二 //GetType的参数需是类的完整名称,带命名空间 Type type = Type.GetType(typeName, true); IPark park = Activator.CreateInstance(type) as IPark; #endregion return park; } }
客户端:
class Program { static void Main(string[] args) { IPark park = ParkFactory.CreatePark("ParkFactorys.ParkA"); park.GetParkInfo(); Console.ReadLine(); } }
4.抽象工厂模式
以上的三种的产品扩展都是针对同一个系列,当有多个时需要工厂里将每个系列一个个的抽象,这时就要抽象工厂模式。
比如如下例子,除了集成停车场,现在也要集成汽车,这时就需要在工厂接口里增加创建汽车接口的方法,抽象工厂是对工厂模式的继续扩充。
public interface IPark { void GetParkInfo(); } public class ParkA : IPark { public void GetParkInfo() { Console.WriteLine("ParkA"); } } public class ParkB : IPark { public void GetParkInfo() { Console.WriteLine("ParkB"); } } public interface ICar { void Run(); } public class CarA : ICar { public void Run() { Console.WriteLine("CarA Run"); } } public class CarB : ICar { public void Run() { Console.WriteLine("CarB Run"); } } public interface IFactory { IPark CreatePark(); ICar CreateCar(); } public class ParkFactoryA : IFactory { public IPark CreatePark() { return new ParkA(); } public ICar CreateCar() { return new CarA(); } } public class ParkFactoryB : IFactory { public IPark CreatePark() { return new ParkB(); } public ICar CreateCar() { return new CarB(); } }
客户端:
class Program { static void Main(string[] args) { IFactory factory = new ParkFactoryA(); IPark park=factory.CreatePark(); park.GetParkInfo(); ICar car = factory.CreateCar(); car.Run(); Console.ReadLine(); } }
最后简单的总结:
简单工厂:简单实用,但违反开放封闭;
工厂方法:开放封闭,单一产品;
抽象工厂:开放封闭,多个产品;
反射工厂:可以最大限度的解耦。
源码下载