人物:大鸟,小菜
事件:小菜找大鸟聊天,大鸟了解到小菜同学薛磊风因车祸,腿受伤住院,小菜去医院探望他时发现,薛磊风几年以来一直帮助一个孤寡老人,为老人洗衣扫地,买米买油,现在因为出了意外不能帮助老人了,所以薛磊风委托小菜和同学们在这段时间里,帮忙照顾老人
工厂方法模式:
1.回顾简单工厂模式
2.了解工厂方法模式
3.分析简单工厂模式和工厂方法模式的区别,提出工厂方法模式的缺点
4.通过结合学雷锋做好事的案例,做出雷锋工厂,分析了工厂方法模式的优点,然后解释清楚了第3点提出的所谓的缺点,并说明这个“缺点”在后续会用其他方式解决
回顾简单工厂模式
工厂类实现:
public class OperationFactory { public static Operation createOperate(String operate) { Operation oper = null; switch (operate) { case "+": oper = new OperationAdd(); case "-": //oper = new OperationSub(); case "*": //oper = new OperationMul(); case "/": //oper = new OperationDiv(); break; } return oper; } }
客户端的应用:
public static void main(String[] args) { Operation oper; oper = OperationFactory.createOperate("+"); oper.setNumberA(new BigDecimal("1")); oper.setNumberB(new BigDecimal("2")); String result = oper.getResult(); log.info("运算结果为:{}", result); }
工厂方法模式实现
工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到子类
具体实现:
先构建一个工厂接口
public interface IFactory { Operation createOperation(); }
然后加减乘除各建一个具体工厂去实现这个接口
public class AddFactory implements IFactory { @Override public Operation createOperation() { return new OperationAdd(); } } public class SubFactory implements IFactory { @Override public Operation createOperation() { return new OperationSub(); } } ......
客户端实现:
public static void main(String[] args) { IFactory operFactory = new AddFactory(); Operation oper = operFactory.createOperation(); oper.setNumberA(new BigDecimal("1")); oper.setNumberB(new BigDecimal("2")); String result = oper.getResult(); log.info("result:{}", result); }
小菜:关于简单工厂模式和工厂方法模式,我还是有疑问,新加一个开根号的功能,需要修改的代码部分如下,工厂方法模式这不就比简单工厂模式更复杂了么?
简单工厂模式 | 工厂方法模式 |
1.先加一个开根号的功能类 | 1.加功能类 |
2.然后去加工厂方法,在里面的Case里面加开根号的逻辑 | 2.加工厂类 |
3.改客户端代码 |
大鸟:是的,这也就是简单工厂模式和工厂方法模式的区别所在
简单工厂模式
1.最大优点:是在工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态地实例化相关的类,对于客户端来说,去除了与具体产品的依赖
2.例如:像计算器,把"+"传给工厂,工厂自动实例出对象,客户端只要做运算即可
3.缺点:这里的问题在于,要增加一个开根号的功能,要去修改"Case"里的逻辑,所以不仅对扩展开放了,而且对修改也开放了,违反了开放-封闭原则
工厂方法模式
1.优点:因为将实例化对象延迟到了子类中进行,所以新加功能只要增加子类和工厂即可,满足开放-封闭原则
2.小菜对工厂方法模式的疑惑:因为工厂方法模式的实现,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说把以前"Case"的内容从工厂类移到了客户端,这不还是没有解决么???
大鸟:那我们带着问题,再来看雷锋做好事的例子
雷锋工厂之简单工厂模式的实现
雷锋类:
@Slf4j public class LeiFeng { public void sweep() { log.info("扫地"); } public void wash() { log.info("洗衣"); } public void bugRice() { log.info("买米"); } }
学雷锋的大学生:
public class UnderGraduate extends LeiFeng { }
学雷锋的社区志愿者:
public class Volunteer extends LeiFeng { }
简单工厂类:
public 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; } }
客户端代码:(这里可以看到分别实例化的时候,三段代码重复了)
public static void main(String[] args) { LeiFeng studentA = SimpleFactory.createLeiFeng("学雷锋的大学生"); studentA.bugRice(); LeiFeng studentB = SimpleFactory.createLeiFeng("学雷锋的大学生"); studentB.sweep(); LeiFeng studentC = SimpleFactory.createLeiFeng("学雷锋的大学生"); studentC.wash(); }
雷锋工厂之工厂方法模式的实现
雷锋工厂接口:
public interface LeiFengFactory { LeiFeng createLeiFeng(); }
学雷锋的大学生工厂:
public class UnderGraduateFactory implements LeiFengFactory { @Override public LeiFeng createLeiFeng() { return new UnderGraduate(); } }
学雷锋的社区志愿者工厂:
public class UnderGraduateFactory implements LeiFengFactory { @Override public LeiFeng createLeiFeng() { return new UnderGraduate(); } }
客户端:
public static void main(String[] args) { LeiFengFactory factory = new UnderGraduateFactory(); //要换成社区志愿者,改这一句就行 LeiFengFactory factory = new VolunteerFactory(); LeiFeng student = factory.createLeiFeng(); student.bugRice(); student.sweep(); student.wash(); }
大鸟:这里可以看出,如果要修改客户端,将学习雷锋的大学生换成学习雷锋的社区志愿者,只用修改这一处就行,而简单工厂模式要改三句
小菜:我知道了,工厂方法模式,1是克服了简单工厂模式违反的开放封闭原则,2是保持了封装对象创建过程中的优点(即使要更换对象,不需要做大的改动就可以实现,降低了客户程序和产品对象的耦合),但是还是没有解决客户端判断的问题啊?
大鸟:别急,利用"反射",可以解决这个问题,这个我们后面慢慢道来