题目:“请用c++,java,c#或VB.NET任意一种面向对象语言实现一个计算器控制台程序,要求输入2个数和运算符号,得到结果。”
拿起就干!看我一顿操作猛如虎。代码如下
import java.util.Scanner;
public class Domo01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个数。");
int number1 = scanner.nextInt();
System.out.println("请输入运算符号(+,-,*,/)。");
String operator = scanner.next();
System.out.println("请输入第二个数。");
int number2 = scanner.nextInt();
//结果
double result=0;
if("+".equals(operator)){
result=number1+number2;
}else if("-".equals(operator)){
result=number1-number2;
}else if("*".equals(operator)){
result=number1*number2;
}else if("/".equals(operator)){
result=number1/number2;
}
System.out.println("结果为:"+result);
}
}
是不是觉得很简单?一顿操作下来,十分钟即可完成!
初学者代码毛病
且先不说出题人的用意,单就上面的代码就有很多不足的地方。
可能存在命名不规范,如果用户输入的除数为0或非数字字符怎莫办?
于是你会修改代码,优化代码如下。
import java.util.Scanner;
public class Domo01 {
public static void main(String[] args) {
try {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个数。");
int number1 = scanner.nextInt();
System.out.println("请输入运算符号(+,-,*,/)。");
String operator = scanner.next();
System.out.println("请输入第二个数。");
int number2 = scanner.nextInt();
//结果
double result=0;
if("+".equals(operator)){
result=number1+number2;
}else if("-".equals(operator)){
result=number1-number2;
}else if("*".equals(operator)){
result=number1*number2;
}else if("/".equals(operator)){
result=number1/number2;
}
System.out.println("结果为:"+result);
}catch (Exception e){
System.out.println("你的输入有错"+e.getMessage());
}
}
}
修改得很快哇!至少就目前代码来说实现计算器是莫有问题得。但这样真的符合出题人的用意了吗?
面向对象编程
所有编程初学者都会有这样的问题,就是碰到问题就直觉地用计算机能够理解的逻辑来描述和表达待解决的问题及具体的求解过程。这其实是用计算机的方式去思考,比如计算器这个程序,先要求输入两个数和运算符号,然后根据运算符号判断选择如何运算,得到结果,这本身没有错,但这样的思维却使得我们的程序只为满足实现当前的需求,程序不容易维护,不容易扩展,更不容易复用。从而达不到高质量代码的要求。
面向对象的分析设计编程思想,考虑用封装,继承,多态把程序的耦合度降低。
设计模式使得程序更加灵活,容易修改,并且易于复用。
(如果你对继承,多态,封装,重写方法等的概率不熟悉,需要自行学习相关概念。)
业务的封装
业务逻辑和界面逻辑分开,让它们的耦合度降低。只有分离开,才可以达到易维护或扩展。
现在的你是不是有思路修改代码了,你也许会这样修改。
添加一个Operator运算类,代码如下。
public class Operator {
public static double getResult(int number1,int number2,String operator){
double result=0;
if("+".equals(operator)){
result=number1+number2;
}else if("-".equals(operator)){
result=number1-number2;
}else if("*".equals(operator)){
result=number1*number2;
}else if("/".equals(operator)){
result=number1/number2;
}
return result;
}
}
客户端代码
import java.util.Scanner;
public class Domo02 {
public static void main(String[] args) {
try {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个数。");
int number1 = scanner.nextInt();
System.out.println("请输入运算符号(+,-,*,/)。");
String operator = scanner.next();
System.out.println("请输入第二个数。");
int number2 = scanner.nextInt();
double result = Operator.getResult(number1, number2, operator);
System.out.println("结果为:"+result);
}catch (Exception e){
System.out.println("你的输入有错"+e.getMessage());
}
}
}
这样就完全把业务和界面分离了。现在不管是Windows程序,还Web版程序,PDA,手机等需要运算都可以使用它,
目前就使用的面向对象的三大特征之一,封装。
紧耦合vs松耦合
目前的代码,能否做到很灵活的可修改和扩展?你可能会想:“我都已经把业务逻辑和界面逻辑分离开了,难道还不够灵活吗?”
现在需要添加一个开根sqrt运算,你咋个修改?
你可能会想:就只需要修改Operator类,加一个else if分支即可。
问题是你要加一个平方根运算,却需要让加减乘除的运算都得来参与编译,如果你一不小心,把加法运算改成了减法,这岂不是大大的糟糕。打个比方,如果现在公司要求你为公司的薪资管理系统做维护,原来只有技术人员(月薪),市场销售人员(底薪+提成),经理(年薪+股份)三种运算算法,现在要增加兼职工作人员(时薪)的算法,但按照你的程序写法,公司就必须要把包含原三种算法的运算类给你,让你修改,你如果心中小算盘一打,'TMD,公司给我的工资这么低,我真是郁闷,这下有机会了’,于是你除了增加了兼职算法以外,在技术人员(月薪)算法中写了一句
if(员工是你){
//你的薪水*1.1
}
那就意味着,你的月薪每月都会增加10%(小心被抓去坐牢),本来是让你加一个功能,却使得原有的运行良好的功能代码产生了变化,这个风险太大了。你明白了吗?”
应该把加减乘除等运算分离,修改其中一个不会影响其他几个,增加运算算法也不影响其他的代码。
如何运用继承,多态,你应该有感觉了。
你修改的代码如下
增加运算父类Operator2,代码如下
public abstract class Operator2 {
private int number1;
private int number2;
//set get方法省...
//获取到结果
abstract double getResult();
}
增加加运算OperatorAdd类,代码如下
public class OperatorAdd extends Operator2{
@Override
double getResult() {
return this.getNumber1()+this.getNumber2();
}
}
增加减运算OperatorAdd类,代码如下
public class OperatorSub extends Operator2{
@Override
double getResult() {
return this.getNumber1()-this.getNumber2();
}
}
增加乘运算OperatorMul类,代码如下
public class OperatorMul extends Operator2{
@Override
double getResult() {
return this.getNumber1()*this.getNumber2();
}
}
增加除运算OperatorDiv类,代码如下
public class OperatorDiv extends Operator2{
@Override
double getResult() {
return this.getNumber1()/this.getNumber2();
}
}
这样如果要修改任何一个算法,就不需要提供其他代码了。那么问题来了,你如何让计算器知道运用哪一个算法啦?
简单工厂模式
写得很不错嘛,大大超出我的想象了,你现在的问题其实就是如何去实例化对象的问题,哈,今天心情不错,再教你一招’简单工厂模式’,也就是说,到底要实例化谁,将来会不会增加实例化的对象,比如增加开根运算,这是很容易变化的地方,应该考虑用一个单独的类来做这个创造实例的过程,这就是工厂,来,我们看看这个类如何写。
简单运算工厂类
public class OperatorFactory {
public static Operator2 createOperate(String operator){
Operator2 operator2 = null;
if("+".equals(operator)){
operator2 = new OperatorAdd();
}else if("-".equals(operator)){
operator2 = new OperatorSub();
}else if("*".equals(operator)){
operator2 = new OperatorMul();
}else if("/".equals(operator)){
operator2 = new OperatorDiv();
}
return operator2;
}
}
这样子,你只需要输入运算符号,工厂就会帮你实例化出合适的对象。通过多太,返回父类的方式实现了计算器的结果。
客户端代码
import java.util.Scanner;
public class Domo03 {
public static void main(String[] args) {
try {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个数。");
int number1 = scanner.nextInt();
System.out.println("请输入运算符号(+,-,*,/)。");
String operator = scanner.next();
System.out.println("请输入第二个数。");
int number2 = scanner.nextInt();
Operator2 operator2 = OperatorFactory.createOperate(operator);
operator2.setNumber1(number1);
operator2.setNumber2(number2);
double result = operator2.getResult();
System.out.println("结果为:"+result);
}catch (Exception e){
System.out.println("你的输入有错"+e.getMessage());
}
}
}
编程是一门技术,更加是一门艺术.