提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、题目
请用C++、Java、C#、VB.NET任意一种面向对象的语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。
二、代码1.0
1.1. 代码
代码如下(示例):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Scanner;
@SpringBootApplication
public class FactorymodeApplication {
public static void main(String[] args) {
SpringApplication.run(FactorymodeApplication.class, args);
System.out.println("请输入数字A:");
Scanner s = new Scanner(System.in);
double A = s.nextDouble();
System.out.println("请输入运算符号(+、-、*、/):");
String B = s.next();
System.out.println("请输入数字C:");
double C = s.nextDouble();
double D = 0;
if (B != null && "+".equals(B)){
D = A+C;
}
if (B != null && "-".equals(B)){
D = A-C;
}
if (B != null && "*".equals(B)){
D = A*C;
}
if (B != null && "/".equals(B)){
D = A/C;
}
System.out.println("计算的结果是:" + D);
}
}
1.2. 存在的问题
三、 代码1.1
3.1. 代码
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Scanner;
@SpringBootApplication
public class FactorymodeApplication {
public static void main(String[] args) {
try {
System.out.println("请输入数字A:");
Scanner s = new Scanner(System.in);
double numA = s.nextDouble();
System.out.println("请输入运算符号(+、-、*、/):");
String strOperate = s.next();
System.out.println("请输入数字B:");
double numB = s.nextDouble();
double result = 0;
switch (strOperate){
case "+":
result = numA + numB;
break;
case "-":
result = numA - numB;
break;
case "*":
result = numA * numB;
break;
case "/":
if (numB != 0){
result = numA / numB;
}else {
System.out.println("除数不能为0!");
return;
}
break;
}
System.out.println("计算的结果是:" + result);
} catch (Exception e) {
System.out.println("您的输入有误:"+e.getMessage());
}
}
}
3.2.存在的问题
就目前代码来说,实现计算器没有什么问题,但没有用到面向对象的知识点,题目中说用任意一种面向对象的语言,那就是要用面向对象的方法去实现。
所有编程者都会有这样的问题,就是碰到问题就直觉地用计算机能够理解的逻辑来描述和表达待解决的问题及具体过程。其实是用计算机的方式去思考,比如这个计算器程序,先要求输入两个数和运算符号,然后根据运算符号判断选择如何运算,得到结果,这本身没有错,但是这样的思维却使我们的程序只为满足实现当前的需求,不容易维护和扩展,更不容易复用。
使用面向对象的编程思想,通过封装、继承、多态,把程序的耦合度降到最低,传统印刷术的问题就在于将所有的字都刻在同一版面上造成耦合度太高,用设计模式会使得程序更加灵活,容易修改容易复用。
四、 代码2.0
考虑这样一个问题:如果要再写一个Windows计算器,上面的代码能不能复用呢?
上面的代码哪些是和控制台有关的,哪些是和计算器有关的,我们可以将其进行分类出来,进行业务的封装,就是让业务逻辑与界面逻辑分开,让它们之间的耦合度降低,这样容易维护和扩展。
4.1. 代码
Operation运算类:
public class Operation {
public static double GetOperationResult(double numA, double numB, String strOperate){
double result = 0;
switch (strOperate){
case "+":
result = numA + numB;
break;
case "-":
result = numA - numB;
break;
case "*":
result = numA * numB;
break;
case "/":
result = numA / numB;
break;
}
return result;
}
}
客户端代码:
public class FactorymodeApplication {
public static void main(String[] args) {
try {
System.out.println("请输入数字A:");
Scanner s = new Scanner(System.in);
double numA = s.nextDouble();
System.out.println("请输入运算符号(+、-、*、/):");
String strOperate = s.next();
System.out.println("请输入数字B:");
double numB = s.nextDouble();
double result = Operation.GetOperationResult(numA, numB, strOperate);
System.out.println("计算的结果是:" + result);
} catch (Exception e) {
System.out.println("您的输入有误:"+e.getMessage());
}
}
}
4.2.存在的问题
就目前代码来说,用到了面向对象三大特性中的一个:封装。下面来看看如何利用继承多态来继续完善代码。
五、 代码2.1
需求:现在希望增加一个开根(sqrt)运算,要如何改?
如果在switch中添加分支,却需要让加减乘除都来参与编译,一步小心将加法改成了减法,就非常糟糕了。
现在考虑用继承来解决这个问题。
5.1. 代码
Operation运算类:
import lombok.Data;
//这里使用lombok的注解来省略写get、set等方法
@Data
public abstract class Operation {
private double numA = 0;
private double numB = 0;
public abstract double GetOperationResult();
}
加法运算类:
public class Add extends Operation{
@Override
public double GetOperationResult() {
double result = 0;
result = getNumA() + getNumB();
return result;
}
}
减法运算类:
public class Sub extends Operation {
@Override
public double GetOperationResult() {
double result = 0;
result = getNumA() - getNumB();
return result;
}
}
乘法运算类:
public class Mul extends Operation {
@Override
public double GetOperationResult() {
double result = 0;
result = getNumA() * getNumB();
return result;
}
}
除法运算类:
public class Div extends Operation {
@Override
public double GetOperationResult() {
double result = 0;
if (getNumB() == 0){
try {
throw new Exception("除数不能为0");
} catch (Exception e) {
e.printStackTrace();
}
}else {
result = getNumA() / getNumB();
}
return result;
}
}
Operation工厂类:
public class OperationFactory {
public static Operation createOperate(String operate){
Operation oper = null;
switch (operate){
case "+":
oper = new Add();
break;
case "-":
oper = new Sub();
break;
case "*":
oper = new Mul();
break;
case "/":
oper = new Div();
break;
}
return oper;
}
}
用户界面类:
@SpringBootApplication
public class FactorymodeApplication {
public static void main(String[] args) {
Operation operate = OperationFactory.createOperate("+");
operate.setNumA(1);
operate.setNumB(2);
double result = operate.GetOperationResult();//3.0
}
}
5.2.这样写的好处
不管是控制台程序,还是Windows程序,Web程序,PDA程序,还是手机程序,都可以用这段代码来实现计算器功能,如果需要修改加法程序,只需要修改对应的类即可。如果要增加其他的运算方式,只要增加对应的类,然后修改运算类工厂,添加生成类的分支,如果要修改界面,只要去修改界面即可。
这个是计算器代码的类图,变量名称和方法名称有点不一样,但大体上是这样的一个结构。
下面来介绍一下类图。
六、 类图
6.1.UML类图
6.1.1 类
一个矩形框代表一个类,里面如果写了interface则表示一个接口。
6.1.2 接口
6.1.3 继承:用空心三角形+实现表示
6.1.4 实现接口:用空心三角形+虚线表示
6.1.5 关联关系:用实线箭头表示
6.1.6 聚合关系:用空心菱形+实线箭头表示
6.1.7 合成关系:用实心菱形+实线箭头表示
6.1.8 依赖关系:用虚线箭头表示
总结
所有编程者都会有这样的问题,就是碰到问题就直觉地用计算机能够理解的逻辑来描述和表达待解决的问题及具体过程。其实是用计算机的方式去思考,比如这个计算器程序,先要求输入两个数和运算符号,然后根据运算符号判断选择如何运算,得到结果,这本身没有错,但是这样的思维却使我们的程序只为满足实现当前的需求,不容易维护和扩展,更不容易复用。
使用面向对象的编程思想,通过封装、继承、多态,把程序的耦合度降到最低,传统印刷术的问题就在于将所有的字都刻在同一版面上造成耦合度太高,用设计模式会使得程序更加灵活,容易修改容易复用。
业务的封装,就是让业务逻辑与界面逻辑分开,让它们之间的耦合度降低,这样容易维护和扩展。
不管是控制台程序,还是Windows程序,Web程序,PDA程序,还是手机程序,都可以用这段代码来实现计算器功能,如果需要修改加法程序,只需要修改对应的类即可。如果要增加其他的运算方式,只要增加对应的类,然后修改运算类工厂,添加生成类的分支,如果要修改界面,只要去修改界面即可。
这个是计算器代码的类图,变量名称和方法名称有点不一样,但大体上是这样的一个结构。