一、一道简单的面试题
1、题目:
请用C++、Java、C#任意一种面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。
但凡有一定基础的人,随手一打就能把这题目给解决了,如下:
public static void main(String[] args) {
//模拟数据 从控制台输入的是数字8 ,4 ,+
double num1=8;
double num2=4;
String operator="+";
double result=0.0;
switch(operator)
{
case "+":
result= num1+num2;
break;
case "-":
result=num1-num2;
break;
default :
System.out.println("请输入正确的操作符号");
break;
}
System.out.println(result);
}
2、存在的问题
当我们在面对这些问题时,初学者习惯用计算机的方式去思考,碰到问题就用直觉去用计算机能够理解的逻辑来描述和表达待解决的问题和具体的求解过程。比如上面计算器这个程序,要求输入两个数和运算符号,进行运算得到结果,这本身没有错,但程序不容易维护,不容易扩展,不容易复用。
2.1、业务的封装
我们这段计算的代码,放在windows程序、web程序还是手机项目程序、都应该能够使用其运算逻辑、即业务逻辑和界面逻辑要分开。
针对这样的需求,只要将计算的逻辑代码抽取出来封装成一个专门的运算类:
public class MyCalculatorUtil {
public static double CalculatorTwo(double num1,double num2,String operator)
{
double result=0;
switch(operator)
{
case "+":
result= num1+num2;
break;
case "-":
result=num1-num2;
break;
default :
System.out.println("请输入正确的操作符号");
break;
}
return result;
}
}
2.2、降低耦合
前面已经将业务和界面分离开了,如果说现在 希望能够加一个 开根(sqrt)运算,怎么办?可能你会简单说只要在switch中加一个分支就行了。
那么问题来了,我需要加一个平方根运算,却需要让已经编译好的加减运算都来参与编译。如果一个手误,把已经没问题的加法运算给改错了,重新编译后岂不是大大的糟糕?
打个比方,如果现在公司要求你为公司的薪酬系统做维护,原来只有 技术人员(月薪),市场销售员(底薪+提成),经理(年薪+股份)三种算法,现在要求增加 兼职工作人员(时薪)的算法,如果按照这种程序写法,公司就需要将包含原来的三种算法运算给你,让你修改。那你不仅可以看到这三种职位薪酬算法的运算,还能私自修改其中的代码,这风险太大了。
所以我们需要将 加减乘除运算分离,修改其中一个不会影响另外的几个,增加的运算算法也不会影响到其他的代码。
这就需要使用 继承
多态
来实现降低耦合。
定义抽象类 Calculator ,定义成员变量num1、num2、和抽象方法Calculator(num1,num2)
package base;
public abstract class Calculator {
public double num1;
public double num2;
public Calculator(double num1, double num2) {
super();
this.num1 = num1;
this.num2 = num2;
}
public abstract double counter();
public double getNum1() {
return num1;
}
public void setNum1(double num1) {
this.num1 = num1;
}
public double getNum2() {
return num2;
}
public void setNum2(double num2) {
this.num2 = num2;
}
}
加法运算算法类
package ibase;
import base.Calculator;
public class CalculatorAdd extends Calculator {
public CalculatorAdd(double num1, double num2) {
super(num1, num2);
}
@Override
public double counter() {
return num1+num2;
}
}
减法运算类
package ibase;
import base.Calculator;
public class CalculatorSub extends Calculator{
public CalculatorSub(double num1, double num2) {
super(num1, num2);
}
@Override
public double counter() {
return num1-num2;
}
}
计算器工具类
package util;
import base.Calculator;
import ibase.CalculatorAdd;
import ibase.CalculatorSub;
//传递两个数值,符号,实现计算
public class CalculatorUtil {
public static double CalculatorTwo(double num1,double num2,String operator)
{
switch(operator)
{
case "+":
Calculator c=new CalculatorAdd(num1, num2);
return c.counter();
case "-":
Calculator c1=new CalculatorSub(num1,num2);
return c1.counter();
default :
System.out.println("请输入正确的操作符号");
return 0.0;
}
}
public static void main(String[] args) {
System.out.println(CalculatorUtil.CalculatorTwo(8, 4, "+"));
}
}
这个时候,如果需要我们增加其他运算算法比如:平方根、正弦、立方根等,只需要增加对应的运算子类以及修改运算类工厂即可。
但这还是稍稍有点不足,我们在增加一种算法时,除了增加一个子类外,还需要更改CalculatorUtil的switch代码,这还是有点瑕疵。同样的需求改动,我们当然希望能够花更少的代价做同样的事,这个办法就是反射技术。