故事背景:雷锋依然在人间
概念:
工厂方法(Facotry Method)定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例延迟到其子类。
结构图:
注意点:
(1)工厂方法克服了简单工厂违背开发-封闭原则的缺点,又保持了封装对象的创建过程的优点,集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可实现,降低了客户程序与产品对象的耦合,
(2)缺点是每增加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。
(3)可以利用反射可以解决避免分支判断的问题。
简单工厂与工厂方法的区别:
(1):简单工厂模式的最大优点是在工厂类包含了必要的逻辑判断,根据客户的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖
(2):工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现所需要的类,选择判断的问题还是存在的,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行,需要添加功能,本来是改工厂类的,而现在是修改客户端。,很好的符合“开放-封闭原则”,简单工厂违背了这一个原则。
(3):工厂方法核心是抽象工厂,简单工厂核心是具体的类上。
工厂方法实例解说
工厂方法中,核心工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去实现,这个核心类紧紧负责给出具体工厂必须实现的接口,而不接触哪一个产品被实例化的细节,这使得工厂方法模式可以允许系统在不修改工厂角况下产生新产品,在工厂方法中,工厂类与产品类有平行的等级结构,它们之间一一对应。
<span style="font-size: 18px;">//简单雷锋工厂 class SimpleFactory { public static LeiFeng CreateLeiFeng(string type) { LeiFeng result = null; switch (type) { case "学雷锋的大学生": result = new Undergraduate(); break; case "社区志愿者": result = new Volunteer(); break; } return result; } }</span>
这样的程序结构显然不能符合我们的要求,如果我们增加一种新的类别,我们要修改SimpleFactory类,类随着改变,switch语句在不断的改变,这样使整个应用程序不稳定,进一步分析代码,发现我们可以把学雷锋的大学生和社区志愿者分别当做单独的对象来对待。
我们进一步抽象,为他们抽象出一个共同的 父类,结构图:
代码实现:
<span style="font-size: 18px;">//雷锋工厂 interface IFactory { LeiFeng CreateLeiFeng(); } //学雷锋的大学生工厂 class UndergraduateFactory : IFactory { public LeiFeng CreateLeiFeng() { return new Undergraduate(); } } //社区志愿者工厂 class VolunteerFactory : IFactory { public LeiFeng CreateLeiFeng() { return new Volunteer(); } }</span>
现在是我们需要增加类别,我们只要增加一个继承父类的子类实现即可,而无需修改其他子类,这样的设计满足了类之间的层次关系,又很好的符合了面向对象设计中的单一职责原则,每一个类都要负责一件具体的事情,到这里我们设计的很完美了,我们看如何从客户端来调用:
<span style="font-size: 18px;"> //工厂方法模式 IFactory factory = new UndergraduateFactory(); LeiFeng student = factory.CreateLeiFeng(); student.BuyRice(); student.Sweep(); student.Wash(); Console.Read();</span>
全部代码(一览)
<span style="font-size: 18px;">class Program { static void Main(string[] args) { // //基本方式:薛磊风代表大学生学习雷锋 LeiFeng xueleifeng = new Undergraduate(); xueleifeng.BuyRice(); xueleifeng.Sweep(); xueleifeng.Wash(); LeiFeng student1 = new Undergraduate(); student1.BuyRice(); LeiFeng student2 = new Undergraduate(); student2.Sweep(); LeiFeng student3 = new Undergraduate(); student3.Wash(); //简单工厂模式 LeiFeng studentA = SimpleFactory.CreateLeiFeng("学雷锋的大学生"); studentA.BuyRice(); LeiFeng studentB = SimpleFactory.CreateLeiFeng("学雷锋的大学生"); studentB.Sweep(); LeiFeng studentC = SimpleFactory.CreateLeiFeng("学雷锋的大学生"); studentC.Wash(); //工厂方法模式 IFactory factory = new UndergraduateFactory(); LeiFeng student = factory.CreateLeiFeng(); student.BuyRice(); student.Sweep(); student.Wash(); Console.Read(); } } //雷锋 class LeiFeng { public void Sweep() { Console.WriteLine("扫地"); } public void Wash() { Console.WriteLine("洗衣"); } public void BuyRice() { Console.WriteLine("买米"); } } //学雷锋的大学生 class Undergraduate : LeiFeng { } //社区志愿者 class Volunteer : LeiFeng { } //简单雷锋工厂 class SimpleFactory { public static LeiFeng CreateLeiFeng(string type) { LeiFeng result = null; switch (type) { case "学雷锋的大学生": result = new Undergraduate(); break; case "社区志愿者": result = new Volunteer(); break; } return result; } } //雷锋工厂 interface IFactory { LeiFeng CreateLeiFeng(); } //学雷锋的大学生工厂 class UndergraduateFactory : IFactory { public LeiFeng CreateLeiFeng() { return new Undergraduate(); } } //社区志愿者工厂 class VolunteerFactory : IFactory { public LeiFeng CreateLeiFeng() { return new Volunteer(); } }</span>
实现要点:
1. Factory Method模式的两种情况:一是Creator类是一个抽象类且它不提供它所声明的工厂方法的实现;二是Creator是一个具体的类且它提供一个工厂方法的缺省实现。
2. 工厂方法是可以带参数的。
3. 工厂的作用并不仅仅只是创建一个对象,它还可以做对象的初始化,参数的设置等。
效果:
1. 用工厂方法在一个类的内部创建对象通常比直接创建对象更灵活。
2. Factory Method模式通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系
使用工厂方法的时机:
(1):如果一个类要创建某个接口的对象,但是不知道具体的实现,使用工厂方法模式,把创建对象的工作实例化延迟到子类中去实现
(2):一个类希望由它的子类创建的对象的时候
(3):当类创建对象的职责委托给多个帮助子类中的某一个,并且希望哪一个帮助子类是代理者这一信息局部化的时候
总结:
Factory Method模式是设计模式中应用最为广泛的模式,通过这篇文章的学习相信大家已经对它有了一定的认识。然而我们要明确的是:在面向对象的编程中,对象的创建工作非常简单,对象的创建时机却很重要。Factory Method要解决的就是对象的创建时机问题,它提供了一种扩展的策略,很好地符合了开放封闭原则。