大话设计模式读书笔记(简单工厂模式)
人物:小菜,大鸟
事件:小菜去求职,求职题目是用Java等任意一种面向对象语言,实现一个计算机控制台程序,要求输入两个数和运算符,得到结果
简单工厂模式:
1.小菜实现一道面试题,输入两个数和运算符,得到结果,小菜初次实现后,大鸟指出了3个缺点
2.小菜进行了改进,完成了功能,但思路却不是出题人的思路
3.大鸟为了小菜更好地理解,借曹操吟诗解释了面向对象
4.小菜尝试用封装和继承实现了简单工厂模式,最后让代码达到了灵活性好,可扩展,可复用
小菜的初次实现
@Slf4j public class Program { public static void main(String[] args) { log.info("每输入一个数字或者运算符号请敲回车"); Scanner scanner = new Scanner(System.in); BigDecimal A = scanner.nextBigDecimal(); String calculateChar = scanner.next(); BigDecimal numberB = scanner.nextBigDecimal(); if (calculateChar.equalsIgnoreCase("-")) { log.info(A.subtract(numberB).toString()); } if (calculateChar.equalsIgnoreCase("+")) { log.info(A.add(numberB).toString()); } if (calculateChar.equalsIgnoreCase("*")) { log.info(A.multiply(numberB).toString()); } if (calculateChar.equalsIgnoreCase("/")) { log.info(A.divide(numberB).toString()); } } }
大鸟指出了代码缺点:
1.第一个数字的命名不规范
2.if...else...语句,会无用判断三次
3.如果numberB输入的是0,则代码会报错
小菜的再次尝试
@Slf4j public class Program { public static void main(String[] args) { log.info("每输入一个数字或者运算符号请敲回车"); Scanner scanner = new Scanner(System.in); BigDecimal numberA = scanner.nextBigDecimal(); String calculateChar = scanner.next(); BigDecimal numberB = scanner.nextBigDecimal(); String result = ""; try { switch (calculateChar) { case "+": result = numberA.add(numberB).toString(); case "-": result = numberA.subtract(numberB).toString(); case "*": result = numberA.multiply(numberB).toString(); case "/": if (BigDecimal.ZERO.equals(numberB)) { log.info("除数不能是0"); } else { result = numberA.divide(numberB).toString(); } break; } log.info("最后计算出的数字是:{}", result); } catch (Exception e) { log.info("输入有误,请重新输入"); } } }
大鸟评价:就目前来说,实现功能是没有问题了,但还需确认这样设计是否符合出题人的题意
面向对象编程
作者以曹操写诗,刻板,改诗,刻板,改诗,再刻板举例,改一个字,就需要全部重新刻板,而活字印刷,则改一字就重印一字即可:
1.改字,即只需重新印刷要改之字 --可维护
2.活体印刷这次用了,下次还可以继续使用 --可复用
3.一篇文章若要加字,则再刻一字即可 --可拓展
4.可改变排版格式,横排竖排都可 --灵活性好
小菜尝试将代码复用(业务的封装)
@Slf4j public class Program { public static void main(String[] args) { try { log.info("每输入一个数字或者运算符号请敲回车"); Scanner scanner = new Scanner(System.in); BigDecimal numberA = scanner.nextBigDecimal(); String calculateChar = scanner.next(); BigDecimal numberB = scanner.nextBigDecimal(); String result = getResult(numberA, numberB, calculateChar); if (StringUtils.isEmpty(result)) { log.info("运算符请输入 + - * 或 /"); } else { log.info("最后计算出的数字是:{}", result); } } catch (Exception e) { log.info("输入有误,请重新输入"); } } public static String getResult(BigDecimal numberA, BigDecimal numberB, String calculateChar) { switch (calculateChar) { case "+": return numberA.add(numberB).toString(); case "-": return numberA.subtract(numberB).toString(); case "*": return numberA.multiply(numberB).toString(); case "/": if (BigDecimal.ZERO.equals(numberB)) { log.info("除数不能是0"); } else { return numberA.divide(numberB).toString(); } break; } return null; } }
大鸟:
业务和界面已经分离,现在已经用了面向对象三大特性之一的封装了,那么另外两个继承和多态改怎么运用到这个例子中呢?
现在要求你加一个开根号的运算符
小菜:
那直接在switch上加一个开根号就行(其实以前我也是,有什么需要就加什么,完全没有考虑过设计模式的问题,使得代码很长又不好维护)
大鸟:
原来getResult()接口的内容被暴露了出来,增加了风险,可能不小心修改错误会导致一大片逻辑错误
小菜尝试用继承方式实现
@Data public class Operation { private BigDecimal numberA = BigDecimal.ZERO; private BigDecimal numberB = BigDecimal.ZERO; public String getResult() { BigDecimal result = BigDecimal.ZERO; return result.toString(); } }
将加减乘除分出来封装好(加法如下),依次继承,供以调用,如果要增加开根号,则再写一个子类即可
public class OperationAdd extends Operation { @Override public String getResult() { BigDecimal result = getNumberA().add(getNumberB()); return result.toString(); } }
大鸟:思路不错,接下来就是考虑如何实例化对象的问题了,下面是简单工厂模式,可以参考解决这个问题
简单工厂类:
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); }
大鸟:就这样,只要输入运算符号,工厂就能实例化对象,通过多态,返回父类的方式得到结果
大鸟:编程是一门技术,更加是一门艺术,要考虑让代码更加简练,更容易维护,容易拓展和复用。