《大话设计模式》之--第8章 雷锋依然在人间----工厂方法模式

8 雷锋依然在人间----工厂方法模式

8.1再现活雷锋

小菜来找大鸟,说:“今天我们见到活雷锋了。”

“哦,”大鸟感兴趣道,“现在已经很少提这个人名了,说说看。”

“我们班有个同学叫薛磊风,昨天,他出了车祸,被车撞断了腿,医生说没大碍,可以恢复。”小菜说,“四年来,他做人很低调,只听说他时常去勤工俭学,周末一般就看不到他,别的也没感觉到什么。”

“他不太走运,不过所谓‘常在路上走,哪有不碰车。’被车撞也是没办法的。”

“是呀,我们今天白天去医院看望他,却听到了一个非常感人的故事,应该说,像是在电视里才能看到的真人真事。”

“他做了感人的好事了!

“你怎么知道,哦,我告诉你见到活雷锋的。你别打岔。”小菜不满地说,“是这样的,他这几年,都一直在帮助一个孤寡老人,每周都去老人家里,为老人洗衣扫地、买米买油,三年多,一直这样,因为他家住在广西很偏远的地方,到了南宁还要再坐几天的汽车,所以他四年来没有回过老家,也正因为此,他对这位老人家的帮助从没有停止过。”

“可是现在出了意外,他住进医院,不能帮助他人了。”大鸟还是打岔道,“这种事你骗谁呀,这么多孤寡老人,他为什么只帮助这一位?

“你再打岔我就不说了,你不信也听听,接受一下再教育,我今天是受教育了。”小菜接着说。“他在大一的时候,从收音机里听到说,有位孤老早年丧妻,而唯一拉扯大的儿子在八十年代中越战争时牺牲了,而他正是从小生活在离战争不远的边防小镇,目睹过战争的残酷和解放军的无私奉献。所以他一听到这个消息就主动去为老人提供帮助。老人身体不好,尽管有些社会的救济,但在生活上还是不太方便,于是他帮助老人就成了每周末的功课。”

“哦,奉献精神代代传呀。”

“现在他住院了,至少近几个月都没办法去老人家,所以他委托我们去帮他继续做这件好事,此时我们大家才知道,雷锋原来就在我们身边。”

“不容易,在21世纪,我还以为这样的人越来越少了呢,原来乐于助人的事迹还是比比皆是。”

“明天我和几名同学就去老人家。不过老人不认识我们,薛磊风也交待我们不要提任何人的名字,就像他一样说是学雷锋做好事就行了。”

“做了好事不留名,坚持不懈三年多,真让人佩服呀,受教育了。”大鸟感慨地说。

8.2简单工厂模式实现

“好了,我来是为了请教你一个问题,这几天一直在研究工厂方法模式,但还是不太理解它和简单工厂的区别,感觉还不如简单工厂方便,为什么要用这个模式,到底这个模式的精翻在哪里?”

“哈,你刚才讲的故事不就是最好的工厂方法的样例吗?”

“薛磊风的事?怎么讲?”

“那你先把简单工厂模式和工厂方法模式的典型实现说给我听听。”

“哦,简单工厂模式实现是这样的。”

“首先简单工厂模式,若以我写的计算器为例,结构图如下。”

 

《大话设计模式》之--第8章 雷锋依然在人间----工厂方法模式_第1张图片

//简单运算工厂类 public class OperationFactory { public static Operation createOperation(String operate) { Operation oper = null; if ("+".equals(operate)) oper = new OperationAdd(); else if ("-".equals(operate)) oper = new OperationSub(); else if ("*".equals(operate)) oper = new OperationMul(); else if ("/".equals(operate)) oper = new OperationDiv(); return oper; } } //客户端类 Operation oper = OperationFactory.createOperation(operator); oper.setNumberA(numberA); oper.setNumberB(numberB); double result = oper.getResult();

8.3工厂方法模式实现

“那如果换成工厂方法模式来写这个计算器,你能写吗?”

“当然可以,就是因为我写出来了,才感觉好像工厂方法没什么好处!我写给你看。”

“计算器的工厂方法模式实现的结构图是这样的。”

《大话设计模式》之--第8章 雷锋依然在人间----工厂方法模式_第2张图片

//工厂接口 public interface IFactory { Operation createOperation(); } //加减乘除各建立一个具体工厂去实现这个接口 public class AddFactory implements IFactory { public Operation createOperation() { return new OperationAdd(); } } public class SubFactory implements IFactory { public Operation createOperation() { return new OperationSub(); } } public class MulFactory implements IFactory { public Operation createOperation() { return new OperationMul(); } } public class DivFactory implements IFactory { public Operation createOperation() { return new OperationDiv(); } } //客户端的代码 public class Main { public static void main(String[] args) { IFactory operFactory = new AddFactory(); Operation oper = operFactory.createOperation(); oper.setNumberA(1); oper.setNumberB(2); try { double result = oper.getResult(); System.out.println("结果是:" + result); } catch (Exception e) { e.printStackTrace(); } } }

“对啊,写的很好。”大鸟说,“工厂方法模式就是这样写的,你有什么问题?”

8.4简单工厂VS工厂方法

“怪就怪在这里啊,以前我们不是说过嘛,如果我现在需要增加其他运算,比如求M数的N次方,或者求M数的N次方根,这些功能的增加,在简单工厂里,我是先去加‘求M数的N次方’功能类,然后去更改工厂方法,当中加Caseif语句来做判断,现在用了工厂方法,加功能类没问题,再加相关的工厂类,这也没问题,但要我再去更改客户端,这不等于不但没有简化难度,反而增加了很多类和方法,把复杂性增加了嘛?为什么要这样?”

“问的好,这其实就是工厂方法模式和简单工厂的区别所在。简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。就像你的计算器,让客户端不用管该用哪个类的实例,只需要把‘+’给工厂,工厂自动就给出了相应的实例,客户端只要去做运算就可以了,不同的实例会实现不同的运算。但问题也就在这里,如你所说,如果要加一个‘求M数的N次方’的功能,我们是一定需要给运算工厂类的方法里加Caseif的分支条件的,修改原有的类?这可不是好办法,这就等于说,我们不但对扩展开放了,对修改也开放了,这样就违背了什么原则?”

“哦,是的,违背的是开放-封闭原则。”

“对,于是工厂方法就来了。”

工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。工厂方法模式结构图。

《大话设计模式》之--第8章 雷锋依然在人间----工厂方法模式_第3张图片

“我们讲过,既然这个工厂类与分支耦合,那么我就对它下手,根据依赖倒转原则,我们把工厂类抽象出一个接口,这个接口只有一个方法,就是创建抽象产品的工厂方法。然后,所有的要生产具体类的工厂,就去实现这个接口,这样,一个简单工厂模式的工厂类,变成一个工厂抽象接口和多个具体生成对象的工厂,于是我们要增加‘求M数的N次方’的功能时,就不需要更改原有的工厂类了,只需要增加此功能的运算类和相应的工厂类就可以了。”

《大话设计模式》之--第8章 雷锋依然在人间----工厂方法模式_第4张图片

“这样整个工厂和产品体系其实都没有修改的变化,而只是扩展的变化,这就完全符合了开放-封闭原则的精神。”

“哦,工厂方法从这个角度来讲,的确要比简单工厂模式来得强。”

“其实你仔细观察就会发现,工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂内部的逻辑判断移到了客户端代码来进行。你想要加功能,本来是改工厂类的,而现在要改客户端了!”

“这也是我困惑的地方。”

8.4雷锋工厂

“这个我们过会再讲,以你刚才讲的故事来说吧,雷锋是人人皆知的做好人好事的模范,而班级的这位薛磊风同学以学习雷锋的名义做好事,而你们现在需要去代替他做好事,这其实就是典型的工厂方法模式应用了。”

“哦,你说来听听。”

“首先,薛磊风作为一个大学生,以学雷锋做好事的名义去帮助老人做事,这里如何设计?”

“我的想法是这样的:雷锋类拥扫地、洗衣、买米等方法。”

//雷锋 public class LeiFeng { public void sweep() { System.out.println("扫地"); } public void wash() { System.out.println("洗衣"); } public void buyRice() { System.out.println("买米"); } } //学雷锋的大学生类,继承雷锋 public class Undergraduate extends LeiFeng { } //客户端代码 public class Main { public 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();

“你们都是要毕业的,而帮助老人却是长期工作,所以‘社区志愿者’更合适,此时这样的写法就非常的不合适了,因为我们需要更改多个实例化的地方。”

“是啊,其实老人不需要知道是谁来做好事,他只需要知道是学雷锋的人来帮忙就可以了。所以还需要增加一个继承‘雷锋’类的‘社区志愿者’类。”

//社区志愿者 public class Volunteer extends LeiFeng { } //简单工厂类 public class SimpleFactory { public static LeiFeng createLeiFeng(String type) { LeiFeng result = null; if ("学雷锋的大学生".equals(type)) { result = new Undergraduate(); } else if ("社区志愿者".equals(type)) { result = new Volunteer(); } return result; } } //客户端代码,如果要换,就只需要换“学雷锋的大学生”为“社区志愿者” LeiFeng studentA = SimpleFactory.createLeiFeng("学雷锋的大学生"); studentA.buyRice(); LeiFeng studentB = SimpleFactory.createLeiFeng("学雷锋的大学生"); studentB.sweep(); LeiFeng studentC = SimpleFactory.createLeiFeng("学雷锋的大学生"); studentC.wash();

“好,此时你就发现了,你需要在任何实例化的时候写出这个工厂的代码。这里有重复,也就有了坏味道。你再用工厂方法模式写一遍。”

 

//雷锋工厂 public interface IFactory { LeiFeng createLeiFeng(); } //学雷锋的大学生工厂 public class UndergraduateFactory implements IFactory { public LeiFeng createLeiFeng() { return new Undergraduate(); } } //社区志愿者工厂 public class VolunteerFactory implements IFactory { public LeiFeng createLeiFeng() { return new Volunteer(); } } //客户端代码 public class Main { public static void main(String[] args) { IFactory factory = new UndergraduateFactory(); LeiFeng student = factory.createLeiFeng(); student.buyRice(); student.sweep(); student.wash(); } }

“我明白了,尽管如果要换成‘社区志愿者’也还是要修改代码,但是只需要修改一处就可以了,这是最佳的做法。”

“最佳做法?NOPNOPNOP,这不是最好的,不过应该算是比较好的了。你现在明白什么时候用简单工厂模式,什么时候用工厂方法模式了没?”

“我感觉啊,工厂方法模式克服了简单工厂违背开放-封闭原则的缺点,又保持了封闭对象创建过程的优点。”

“说的好,它们都是集中封闭了对象的创建,使得要更换对象时,不需要做大的改动就可以实现,降低客户程序与产品对象的耦合。工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。但缺点是由于每加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。”

“你说这不是最佳做法?难道说还有更好的?还有就是这样还是没有避免修改客户端的代码啊!”

“哈,之前我就提到过,利用‘反射’可以解决避免分支判断的问题。不过今天还是不急,等以后再谈。你明天不是还要去看望老人家吗?洗洗睡吧,这年头,活雷锋太少了,你们班那个薛磊风,实在是太不容易了,要是有雷锋工厂该有多好啊。”

“雷锋工厂!雷锋工厂!是啊,如果每个人都有雷锋精神,这世界该有多么美好啊”小菜念叨着,不觉中唱到:“学习雷锋,好榜样,忠于革命忠于党,爱憎分明不忘本,立场坚定斗志强

 

你可能感兴趣的:(设计模式,exception,String,Class,interface,产品)