一、情景回放:
某校大二软件工程专业班的《Java程序设计》课上,老师给大伙出了一个题目:用java实现一个简单的计算器功能(用户从控制台输入),5分钟不到大部分同学就自信满满地交给了老师,他们的答案大部分是这三种:
二、常见的三种答案
2.1、第一种
用户输入两个需要处理的数,和做什么操作,根据操作符做相应的结果
public static void main(String args[]) { Scanner scn = new Scanner(System.in); System.out.print("第一个数:"); int a = scn.nextInt(); System.out.print("操作(+,-,*,/):"); String option = scn.next(); System.out.print("第二个数:"); int b = scn.nextInt(); int result = 0; if ("+".equals(option)) { result = a + b; } if ("-".equals(option)) { result = a - b; } if ("*".equals(option)) { result = a * b; } if ("/".equals(option)) { result = a / b; } System.out.println("结果:" + result); }
老师点评:根据程序来看if需要判断4次,完全可以再优化一下
2.2、第二种
于是乎另外一批同学乐了,他们的程序是这样的:
public static void main(String args[]) { Scanner scn = new Scanner(System.in); System.out.print("第一个数:"); int a = scn.nextInt(); System.out.print("操作(+,-,*,/):"); String option = scn.next(); System.out.print("第二个数:"); int b = scn.nextInt(); int result = 0; if ("+".equals(option)) { result = a + b; } else if ("-".equals(option)) { result = a - b; } else if ("*".equals(option)) { result = a * b; } else if ("/".equals(option)) { result = a / b; } System.out.println("结果:" + result); }
2.3、第三种
等他们刚说完,另外一个同学马上发声说:还可以用switch语句,而且对于除法,如果除数为0是要报错的,应该加上错误处理?(下面是他的代码)
public static void main(String args[]) { Scanner scn = new Scanner(System.in); System.out.print("请输入第一个数:"); int oNumFirst = scn.nextInt(); System.out.print("请输入操作方式(+,-,*,/):"); String option = scn.next(); System.out.print("请输入第二个数:"); int oNumSecond = scn.nextInt(); int result = 0; switch (option) { case "+": result = oNumFirst + oNumSecond; break; case "-": result = oNumFirst - oNumSecond; break; case "*": result = oNumFirst * oNumSecond; break; case "/": try { result = oNumFirst / oNumSecond; } catch (ArithmeticException e) { e.printStackTrace(); System.out.println("除数不能为0"); } break; } System.out.println("结果为:" + result); }
老师点评:不错,这几位同学在上面几位同学的基础有所改进,并且加上了错误的处理,相对完善了许多(此时这位同学正在沾沾自喜吧);
三、需求改变
老师继续说:现在如果让你们新添加一个开根号(sqrt)的操作你们会怎么做?
刚才那位同学立马回答:在switch语句上加一个分支就可以了;
老师说:你加的时候不小心加错了怎么办?还记得我开学的第二节课就跟你们讲得程序设计的原则是什么吗?
“高内聚、低耦合”(同学们异口同声地回答道)
“还有上周不是讲了面向对象的三大特点吗?是什么?”
“封装、集成、多态”(部分同学回答到)
“那还记得我说的,高内聚、低耦合意味着什么吗?”
四、盼来面向对象
“业务处理和视图分开,以降低耦合,老师桌子上还有一份答案是我写的你看看可以不”(一个同学答到)
(老师看过答案之后)“不错,这位同学懂得了面向对象中封装的意思,如果能把继承和多态的思想加进入就非常好了,我们来看看他的代码”
public class Optioner { private int oNumFirst; private int oNumSecond; private String option; public int getoNumFirst() { return oNumFirst; } public void setoNumFirst(int oNumFirst) { this.oNumFirst = oNumFirst; } public int getoNumSecond() { return oNumSecond; } public void setoNumSecond(int oNumSecond) { this.oNumSecond = oNumSecond; } public String getOption() { return option; } public void setOption(String option) { this.option = option; } public Optioner() { } public Optioner(int oNumFirst, int oNumSecond, String option) { this.oNumFirst = oNumFirst; this.oNumSecond = oNumSecond; this.option = option; } public int getReuslt() { int result = 0; switch (option) { case "+": result = oNumFirst + oNumSecond; break; case "-": result = oNumFirst - oNumSecond; break; case "*": result = oNumFirst * oNumSecond; break; case "/": try { result = oNumFirst / oNumSecond; } catch (ArithmeticException e) { e.printStackTrace(); System.out.println("除数不能为0"); } break; } return result; } }
视图、客户端
public static void main(String args[]) { Scanner scn = new Scanner(System.in); System.out.print("请输入第一个数:"); int oNumFirst = scn.nextInt(); System.out.print("请输入操作方式(+,-,*,/):"); String option = scn.next(); System.out.print("请输入第二个数:"); int oNumSecond = scn.nextInt(); Optioner option\=new Optioner(oNumFirst,oNumSecond,option); System.out.print("结果为:"+optioner.getReuslt()); }
五、终于等到你
老师刚讲完的同时,一位同学突然举手说:“老师,我做完了”,而这离其他同学交完,整整过去了30分钟。
老师说:“估计这位同学不会让我们失望的!”
“不错,能写到这个程度非常不错了!已经把封装、继承、多态的思想了弄明白了”
/** * 计算器类 * Created by HDL on 2016/11/30. */public class Optioner { private int oNumFirst;//第一个数 private int oNumSecond;//第二个数 public int getoNumFirst() { return oNumFirst; } public void setoNumFirst(int oNumFirst) { this.oNumFirst = oNumFirst; } public int getoNumSecond() { return oNumSecond; } public void setoNumSecond(int oNumSecond) { this.oNumSecond = oNumSecond; } public Optioner() { } public Optioner(int oNumFirst, int oNumSecond) { this.oNumFirst = oNumFirst; this.oNumSecond = oNumSecond; } public int getReuslt() { return 0; } }
/** * 加法运算 * Created by HDL on 2016/11/30. */public class AddOptioner extends Optioner { @Override public int getReuslt() { return getoNumFirst() + getoNumSecond(); } }/** * 减法运算 * Created by HDL on 2016/11/30. */public class SubOptioner extends Optioner { @Override public int getReuslt() { return getoNumFirst() - getoNumSecond(); } }/** * 乘法运算 * Created by HDL on 2016/11/30. */public class MulOptioner extends Optioner { @Override public int getReuslt() { return getoNumFirst() * getoNumSecond(); } }/** * 除法运算 * Created by HDL on 2016/11/30. */public class DivOptioner extends Optioner { @Override public int getReuslt() { if (getoNumSecond() == 0) { System.out.println("除数不能为0"); return -1; } else { return getoNumFirst() / getoNumSecond(); } } }
测试
public static void main(String args[]) { Scanner scn = new Scanner(System.in); System.out.print("请输入第一个数:"); int oNumFirst = scn.nextInt(); System.out.print("请输入操作方式(+,-,*,/):"); String opt = scn.next(); System.out.print("请输入第二个数:"); int oNumSecond = scn.nextInt(); Optioner option\=null; switch (opt) { case "+": option\=new AddOptioner(); break; case "-": option\=new SubOptioner(); break; case "*": option\=new MulOptioner(); break; case "/": option\=new DivOptioner(); break; } optioner.setoNumFirst(oNumFirst); optioner.setoNumSecond(oNumSecond); System.out.println("结果为:" + optioner.getReuslt()); }
“大家看到上面的代码写得很不多了,希望大家都能像这位同学学习,善于理论与实践相结合,下面我在这个同学的代码基础上给你们讲讲今天课堂的主要内容,——–简单工厂模式”
六、进阶
“可以看到这位同学的代码耦合性还是有点高,在测试类(mian)中还是需要判断具体是哪种操作,如果有这么一种类,它可以根据操作符去具体实例化对象是不是就很爽,而且这样可以很好地解耦,来看看实现”
其他类不变,增加一个Optioner的工厂类即可:
/** * optioner工厂类 * Created by HDL on 2016/11/30. */public class OptionerFactory { public static Optioner createOptioner(String opt) { Optioner optioner = null; switch (opt) { case "+": optioner = new AddOptioner(); break; case "-": optioner = new SubOptioner(); break; case "*": optioner = new MulOptioner(); break; case "/": optioner = new DivOptioner(); break; } return optioner; } }
测试类中只需这样写即可:
public static void main(String args[]) { Scanner scn = new Scanner(System.in); System.out.print("请输入第一个数:"); int oNumFirst = scn.nextInt(); System.out.print("请输入操作方式(+,-,*,/):"); String opt = scn.next(); System.out.print("请输入第二个数:"); int oNumSecond = scn.nextInt(); Optioner optioner = OptionerFactory.createOptioner(opt); optioner.setoNumFirst(oNumFirst); optioner.setoNumSecond(oNumSecond); System.out.println("结果为:" + optioner.getReuslt()); }
对于添加开根号功能,直接增加开根号的类并继承Optioner类,在工厂类加上分支判断即可,业务的增加不会影响视图层的功能,大大降低了代码之间的耦合性。
来张图来帮助记忆:
计算器工厂类根据运算类型(如+)返回一个相应的运算类(如AddOptioner),即可得到相应的结果;
七、总结
有人可能会问如果我还有几十个功能需要加进去是不是要加几十个子类?
确实,简单工厂模式也是有适用访问的,所以你在使用这个模式之前,需要考虑算法是否会经常性的变动,如果变动频繁就不要使用这么模式。(那用什么模式呢?看下一篇策略模式)
简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
还没有评论,来说两句吧...