设计模式之工厂方法(FactoryMethod)模式
在五大设计原则的基础上经过GOF(四人组)的总结,得出了23种经典设计模式,其中分为三大类:创建型(5种)、结构型(7种)、行为型(11种)。今天对创建型中的工厂方法(FactoryMethod)模式的思想进行了一下复习和实践,在此也做一下记录。同样,理解或实践不到位的地方,希望走过路过的看官指正一下!
同样先来看看工厂方法(FactoryMethod)模式的定义:
Define an interface for creating an object, but let subclasses decide which class to instantiate.
意思就是说:定义一个用于创建对象的接口,但让子类决定要实例化哪个类。
也就是工厂方法(FactoryMethod)模式允许将产品类的实例化推迟到具体的创建者子类,由创建者子类决定实例化哪一个产品类。我们同样以汽车的生产作为讲解该模式的例子,因为汽车生产从宏观上来说也是特别符合这个模式的。我要一辆汽车,什么品牌的我没有要求,符合是汽车这个标准就行了。那么世面上不同的汽车生产商就会用自己的生产流程生产出符合汽车这个标准的不同品牌汽车。同样,我们也撸起袖子码一码!因为该模式没有强调待生产的产品类是复杂的,同样也为了减少大家的代码阅读,这次我们把汽车相关类定义的更简单一点!
由于是让子类决定要实例化哪个产品类,那么高层的类或客户代码肯定是不需要知道有哪些具体产品类的,也就是得为众多具体的产品类制定一个产品标准(可以是接口,也可以是抽象类),这里我们定义一个汽车接口——ICar,它只有一个启动接口方法:
/// <summary> /// 汽车接口 /// </summary> public interface ICar { /// <summary> /// 启动汽车 /// </summary> /// <returns>是否启动成功</returns> bool Start(); }
有了汽车产品标准(ICar),我们在此基础上定义两个实现了该标准的具有汽车产品类——奔驰(Benz)和宝马(BWM),它们对汽车产品标准中的启动标准做了不一样的实现(机械钥匙启动和按键启动):
/// <summary> /// 奔驰汽车 /// </summary> [Serializable] public class BenzCar : ICar { private readonly string name = "Benz"; private readonly string model; /// <summary> /// 车名 /// </summary> public string Name { get { return name; } } /// <summary> /// 型号 /// </summary> public string Model { get { return model; } } /// <summary> /// 初始化奔驰汽车 /// </summary> public BenzCar() { } /// <summary> /// 初始化奔驰汽车(带简单参数) /// </summary> /// <param name="model"></param> public BenzCar(string model) { this.model = model; } /// <summary> /// 启动奔驰汽车 /// </summary> /// <returns>是否启动成功</returns> public bool Start() { Console.WriteLine("【{0} {1}】通过机械钥匙启动!", Name, Model); return true; } } /// <summary> /// 宝马汽车 /// </summary> [Serializable] public class BwmCar : ICar { private readonly string name = "BWM"; private readonly string model; /// <summary> /// 车名 /// </summary> public string Name { get { return name; } } /// <summary> /// 型号 /// </summary> public string Model { get { return model; } } /// <summary> /// 初始化宝马汽车 /// </summary> public BwmCar() { } /// <summary> /// 初始化宝马汽车(带简单参数) /// </summary> /// <param name="model"></param> public BwmCar(string model) { this.model = model; } /// <summary> /// 启动宝马汽车 /// </summary> /// <returns>是否启动成功</returns> public bool Start() { Console.WriteLine("【{0} {1}】通过按键启动!", Name, Model); return true; } }
有了汽车产品标准,也有了两个具体的汽车产品类型,生产商还没有,生产标准也还没有!回顾下工厂方法模式的定义:定义一个用于创建对象的接口,但让子类决定要实例化哪个类!这里的“用于创建对象的接口”其实就是生产标准,而“子类”就是实现了生产标准的具体生产商。在我们的例子中就是汽车生产者接口(标准)和实现了该标准的具体汽车生产商:
/// <summary> /// 汽车生产接口(标准、规范) /// </summary> public interface ICarCreator { /// <summary> /// 工厂级别的汽车生产接口方法(工厂方法模式的精髓所在!) /// </summary> /// <returns>生产好的汽车</returns> ICar ProduceCar(); } /// <summary> /// 奔驰生产者 /// </summary> public class BenzCreator : ICarCreator { /// <summary> /// 奔驰生产方法(对生产过程只做了简单模拟) /// </summary> /// <returns>奔驰汽车</returns> public ICar ProduceCar() { ICar car = new BenzCar("梅赛德斯-AMG GLC 43 4MATIC"); Console.WriteLine("【Benz 梅赛德斯-AMG GLC 43 4MATIC】生产开始:\r1、生产发动机;\r2、生产底盘;\r3、生产车身;\r4、生产变速箱;\r5、生产轮胎;\r6、组装;"); Console.WriteLine("【Benz 梅赛德斯-AMG GLC 43 4MATIC】生产完成!"); return car; } } /// <summary> /// 宝马生产者 /// </summary> public class BwmCreator : ICarCreator { /// <summary> /// 宝马生产方法(对生产过程只做了简单模似) /// </summary> /// <returns>宝马汽车</returns> public ICar ProduceCar() { ICar car = new BwmCar("X7"); Console.WriteLine("【BWM X7】生产开始:\r1、生产底盘;\r2、生产发动机;\r3、生产变速箱;\r4、生产车身;\r5、生产轮胎;\r6、组装;"); Console.WriteLine("【BWM X7】生产完成!"); return car; } }
我们可以看到,对于汽车生产标准,实现了该标准的不同汽车生产者可以自行决定生产什么类型的汽车产品以及如何生产——让子类决定要实例化哪个类!
到目前为止,工厂方法(FactoryMethod)模式中各个角色都已经出现:用于创建对象的接口——汽车生产者接口(ICarCreator);子类(即具体的创建者)——奔驰生产者(BenzCreator)和宝马生产者(BwmCreator);要实例化的类(产品对象)——遵循统一产品标准(ICar)的奔驰汽车(BenzCar)和宝马汽车(BwmCar)。
接下来我们来测试一下这样的对象创建构造符不符合工厂方法(FactoryMethod)模式的定义:定义一个用于创建对象的接口,但让子类决定要实例化哪个类。
[TestClass] public class FactoryMethodTest { [TestMethod] public void TestFactoryMethod() { ICarCreator carCreator = new BenzCreator();//先将汽车创建者指向奔驰(Benz)汽车的创建者 ICar car = carCreator.ProduceCar();//由奔驰(Benz)汽车创建者决定生产什么车(实例化哪个类) Assert.IsTrue(car.Start()); Console.Write("\r\r"); carCreator = new BwmCreator();//再将汽车创建者指向宝马(BWM)汽车的创建者 car = carCreator.ProduceCar();//再由宝马(BWM)汽车创建者决定生产什么车(实例化哪个类) Assert.IsTrue(car.Start()); } }
可以看到客户代码已经可以让子类决定要实例化哪个类。我们最后来看看测试输出结果:
最后,再次注意一下工厂方法(FactoryMethod)模式和构建者(Builder)模式的区别:
- 对要生产的产品制定了产品标准(IProduct),构建者模式中这个并不是必要的,当然也可以制定;
- 产品创建者标准(ICreator)只提供一个粗粒度的标准产品生产接口(FactoryMethod);
- 没有了额外的生产指导者,产品的具体创建流程和细节由具体的产品创建者(ConcreteCreator)自己决定;
工厂方法(FactoryMethod)模式的使用场景在于模式定义的后半句:让子类决定要实例化哪个类(子类是指创建者子类)。也就是当你在开发过程中,对于创建对象有这样的需求时可以考虑一下工厂方法模式!而构建者模式主要使用场景在于用相同的构建过程构建复杂对象的不同表示。