序言
在一个宁静的午后,我有幸拜读了程杰大鸟的《大话设计模式》
觉得这是一本不可多得的好书
奈何里面都是C++代码写的示例,对于学Java的同学不是很友好
于是想将书中的核心提炼出来并结合Java示例与大家分享
并且加入一些我曾在生产环境下的应用来“学以致用”
这是第一次开始写CSDN专栏,内容会持续更新,感兴趣的小伙伴可以来个三连
本人水平有限,难免会有不足之处,希望大佬们不吝赐教!
张三去面试,面试官让他用Java实现一个计算器,要求能实现加减乘除操作,张三一想:太简单了!
于是撸起袖子三下五除二写了以下代码
Java实现简单的计算器
public class Calc {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入数字A");
String A = scanner.nextLine();
System.out.println("请选择运算符号");
String B = scanner.nextLine();
System.out.println("请输入数字B");
String C = scanner.nextLine();
String D = null;
if (B.equals("+")) D = (Double.valueOf(A) + Double.valueOf(C)) + "";
if (B.equals("-")) D = (Double.valueOf(A) - Double.valueOf(C)) + "";
if (B.equals("*")) D = (Double.valueOf(A) * Double.valueOf(C)) + "";
if (B.equals("/")) D = (Double.valueOf(A) / Double.valueOf(C)) + "";
System.out.println("计算结果为:" + D);
}
}
实现效果
这段代码能实现基本的功能,但是仍然存在问题:
张三确实意识到不妥,于是改进代码如下
Java改进后的计算器
public class Calc {
public static void main(String[] args) {
try{
Scanner scanner = new Scanner(System.in);
System.out.println("请输入数字A");
String numA = scanner.nextLine();
System.out.println("请选择运算符号");
String operate = scanner.nextLine();
System.out.println("请输入数字B");
String numB = scanner.nextLine();
String result = null;
switch (operate){
case "+":
result = (Double.valueOf(numA) + Double.valueOf(numB)) + "";
break;
case "-":
result = (Double.valueOf(numA) - Double.valueOf(numB)) + "";
break;
case "*":
result = (Double.valueOf(numA) * Double.valueOf(numB)) + "";
break;
case "/":
if(numB.equals("0")){
System.out.println("除数不能为0!");
return;
}
result = (Double.valueOf(numA) / Double.valueOf(numB)) + "";
}
System.out.println("计算结果为:" + result);
}catch (Exception e){
System.out.println("输入有误,请检查");
}
}
}
张三心想: 这回你不能难为我了吧!
面试官: 在你的代码里没有看到 面向对象编程思想
张三: ¥%*&……(脸上笑嘻嘻)
何为面向对象编程?
相信这也是很多初入职场的小伙伴被面试官问到频次较高的一个问题
张三: “面向对象就是封装、继承、多态嘛”
面试官: “那为什么要有封装、继承、多态?”
张三: ¥%*&……(脸上笑嘻嘻)
以下内容纯属虚构
相传,苏东坡与他的妹妹苏小妹及诗友黄山谷一起论诗,互相题试。小妹说出“轻风细柳”和“淡月梅花”后,要哥哥从中各加一字,说出诗眼。苏东坡当即道:前者加“摇”,后句加“映”,即成为“轻风摇细柳,淡月映梅花”。
在一旁的黄山谷便吩咐下人将这句话刻字印刷,不多时,匠人将刻好的石板献上。
不料苏小妹却评之为“下品”。苏东坡认真的思索后,得意的说:“有了,轻风舞细柳,淡月隐梅花”。
在一旁的黄山谷便吩咐下人将这句话刻字印刷,又过了不多时,匠人将刻好的石板献上。
谁知小妹微笑道:“好是好了,但仍不属上品。”一旁的黄山谷忍不住了,问道:“依小妹的高见呢?”苏小妹便念了起来:“轻风扶细柳,淡月失梅花。”苏东坡、黄山谷吟诵着,玩味着,不禁击节称妙。
在一旁的黄山谷看向一旁的匠人。
那么这个里面的问题出在哪里呢?
是因为当时还没有活字印刷术,所以每次都需要整版重新雕刻,十分费时费力
如果有活字印刷,每次只需要改动1~2个字即可进行印刷,不需要推倒重来
“第一,要改,只需更改要改之字,此为可维护;第二,这些字并非用完这次就无用,完全可以在后来的印刷中重复使用,此乃可复用;第三,此诗若要加字,只需另刻字加入即可,这是可扩展;第四,字的排列其实可能是竖排可能是横排,此时只需将活字移动就可做到满足排列需求,此是灵活性好。”
“而在活字印刷术出现之前,上面的四种特性都无法满足,要修改,必须重刻,要加字,必须重刻,要重新排列,必须重刻,印完这本书后,此版已无任何可再利用价值。
——《大话设计模式》
听完这个故事,我们大概知道了匠人的苦衷,也找到了解决的方法
软件开发亦复如是
我们就是软件的匠人
以前我们的软件不容易维护升级,没有复用
但是通过封装、继承、多态把程序的耦合度降低,并结合设计模式将程序变得更灵活、易于修改和复用。
这才是面向对象编程思想的核心。
也是软件工程的核心——“高内聚,低耦合”。
如果将来计算器要升级,新增一个“开根”和“平方”运算
首先,为了达到可维护可扩展的目的
我们将界面逻辑和业务逻辑划分开来
张三: “我试试”
面向对象Java计算器第一版
public class Calc {
/**
* @Description TODO 视图层
* @author LaoQin
* @date 2020/03/10
* @param args
* @return void
*/
public static void main(String[] args) {
try{
Scanner scanner = new Scanner(System.in);
System.out.println("请输入数字A");
double numberA = Double.valueOf(scanner.nextLine());
System.out.println("请选择运算符号");
String operate = scanner.nextLine();
System.out.println("请输入数字B");
double numberB = Double.valueOf(scanner.nextLine());
System.out.println("计算结果为:" + getResult(numberA,numberB,operate));
}catch (Exception e){
System.out.println("输入有误,请检查");
}
}
/**
* @Description TODO 逻辑层
* @author LaoQin
* @date 2020/03/10
* @param numberA
* @param numberB
* @param operate
* @return double
*/
public static double getResult(double numberA,double numberB,String operate){
double result = 0;
switch (operate){
case "+":
result = numberA + numberB;
break;
case "-":
result = numberA-numberB;
break;
case "*":
result = numberA*numberB;
break;
case "/":
if(numberB == 0){
System.out.println("除数不能为0!");
throw new RuntimeException();
}
result = numberA/numberB;
}
return result;
}
}
以上代码我们已经运用了“封装”的思想,但是还未用到继承和多态
为了让程序更加“面向对象”
张三又给他们设计了一个父类,并运用了继承
父类Operation
public abstract class Operation {
protected double numberA;
protected double numberB;
/**
* @Description TODO 获取结果
* @author LaoQin
* @date 2020/03/10
* @param
* @return double
*/
public abstract double getResult();
public double getNumberA() {
return numberA;
}
public void setNumberA(double numberA) {
this.numberA = numberA;
}
public double getNumberB() {
return numberB;
}
public void setNumberB(double numberB) {
this.numberB = numberB;
}
}
加法类OperationAdd
public class OperationAdd extends Operation{
@Override
public double getResult() {
return numberA+numberB;
}
}
减法类OperationSub
public class OperationSub extends Operation{
@Override
public double getResult() {
return numberA-numberB;
}
}
乘法类OperationMul
public class OperationMul extends Operation{
@Override
public double getResult() {
return numberA*numberB;
}
}
除法类OperationDiv
public class OperationDiv extends Operation{
@Override
public double getResult() {
if(numberB==0){
throw new RuntimeException();
}
return numberA/numberB;
}
}
但是这时候又出现一个问题:如何让我们的计算器知道我希望使用哪一个算法呢?
这时候就要进入我们今天运用的第一种设计模式——简单工厂模式
工厂,顾名思义,就是一个生产产品的机器,而在设计模式和面向对象编程中
生成的产品就是一个个对象,而这些对象都满足一定的规则(继承同一对象或者实现了同一接口)
我们要根据不同的指令,生产出不同的对象
张三: “我明白了,现在我就造一个<工厂>”
制造不同运算(Operation)的工厂OperationFactory
public class OperationFactory {
public static Operation createOperate(String operate){
//根据不同的运算符生成不同的对象
switch (operate){
case "+":
return new OperationAdd();
case "-":
return new OperationSub();
case "*":
return new OperationMul();
case "/":
return new OperationDiv();
}
return null;
}
}
这样,我们就能根据不同的运算符生成不同的对象,最后通过多态返回父类的方式实现了计算器的结果
Operation op = OperationFactory.createOperate("+");
op.setNumberA(1);
op.setNumberB(2);
double result = op.getResult();
张三: “怎么样面试官,我会用简单工厂模式了,这个面试给过不?”
面试官: “不急,咱们还有第二场面试,敬请期待”
张三: ¥%*&……(脸上笑嘻嘻)