面向对象改造——50道100以内的加减法口算习题
接上篇文章,50道100以内的加减法口算习题的模块化改造基础上,对其进行面向对象的进一步改造
上文链接: link.
文章目录
前言
上文中所提到的模块化是在函数级,通过函数间的交互表示程序结构、分配程序功能,暂时忽略了函数的内部实现。
而本文所讲到的面向对象技术把数据及其操作封装在一个对象中,并把具有相同属性(数据名称)和方法(函数)的所有对象抽象成类,使得能在比函数更抽象、更大程序单元粒度的层次上进行软件开发。
提示:以下是本篇文章正文内容,下面案例可供参考
一、算式类BinaryOperation
1.设计原则
类 BinaryOperation
• 把其中的公共部分抽象出来,把差异部分细化成BinaryOperation 的两个子类:
– 加法算式AdditionOperation
– 减法算式SubstractOperation
• 每个类叧负责完成各自单一的功能,两个子类相互独立。
• 这样的程序结构还便于程序扩展,如增加乘法、除法子类,不影响已经存在的类。
基类BinaryOperation 设计成抽象类。
• 子类AdditionOperation 和SubstractOperation
• 在基类中定义抽象斱法int calculate()返回运算式的计算结果,抽象方法boolean checkingCalculation()检查运算结果。具体实现则分别在两个子类中。
2.UML图
3.代码实现
① BinaryOperation算式类
/**
* 采用面向对象的设计原则,使软件更易扩展、更易更新、更易维护
*/
import java.util.Random;
/**
* 算式类BinaryOperation,抽象父类,封装算式通用的属性和方法
* @author lenovo
*
*/
public abstract class BinaryOperation{
static final int UPPER = 100; //加法约束
static final int LOWER = 0; //减法约束
private int left_operand = 0; // 左操作数
private int right_operand = 0; // 右操作数
private char operator; // 操作符
private int value; // 算式的结果
abstract int calculate(int left, int right); //抽象方法:算式的计算,由子类实现
abstract boolean checkingCalculation(int anInteger); // 抽象方法,检验计算结果,子类负责实现
protected void generateBinaryOperation(char anOperator) {
int left, right, result;
Random random = new Random();
do {
left = random.nextInt(UPPER+1); //产生左操作数
right = random.nextInt(UPPER+1); //产生右操作数
result = calculate(left, right);
}while(!checkingCalculation(result));
left_operand = left;
right_operand = right;
operator = anOperator;
value = result;
}
/**
* 获取左操作数
* @return 获取值
*/
public int getLefOperand() {
return left_operand;
}
/**
* 获取右操作数
* @return 获取值
*/
public int getRightOperand() {
return right_operand;
}
/**
* 获取操作符
* @return 获取值
*/
public char getOperator() {
return operator;
}
/**
* 获取计算结果
* @return 获取值
*/
public int getResult() {
return value;
}
/**
* 判断两个算式是否相等
* @param anOperation:输入待比较算式
* @return 和当前算式比较,如果相等则返回1;如果不相等返回0
*/
public boolean equals(BinaryOperation anOperation) {
return left_operand == anOperation.getLefOperand() &&
right_operand == anOperation.getRightOperand() &&
operator == anOperation.getOperator();
}
/**
* @return 返回a+b 或者 a-b
*/
public String toString() {
String str;
str = String.format("%3d %c %3d ", left_operand, getOperator(), right_operand);
return str;
}
/**
* @return 返回a+b= 或者 a-b=
*/
public String asString() {
return toString() + " = ";
}
/**
* @return 返回a+b=c 或者 a-b=c
*/
public String fullString() {
return toString() + " = " + getResult();
}
}
② AdditionOperation 加法算式子类
/**
* 加法算式子类AdditionOperation,是BinaryOperation的子类
* @author lenovo
*
*/
public class AdditionOperation extends BinaryOperation{
/**
* 加法子类的构造函数
*/
public AdditionOperation() {
generateBinaryOperation('+');
}
/**
* 检查结果约束是否<=UPPER
* @return 如果<=UPPER返回true;否则返回false
*/
public boolean checkingCalculation(int anInteger) {
return anInteger <= UPPER;
}
/**
* 加法计算的实现
* @param left: 左操作数
* @param right: 右操作数
*/
public int calculate(int left, int right) {
return left + right;
}
}
③ SubstractOperation 减法算式子类
/**
* 减法算式子类SubstractOperation,是BinaryOperation的子类
* @author lenovo
*
*/
public class SubstractOperation extends BinaryOperation{
/**
* 减法子类的构造函数
*/
public SubstractOperation() {
generateBinaryOperation('-');
}
/**
* 检查结果约束是否>= LOWER
* @return 如果>= LOWER返回true;否则返回false
*/
public boolean checkingCalculation(int anInteger) {
return anInteger >= LOWER;
}
/**
* 减法计算的实现
* @param left: 左操作数
* @param right: 右操作数
*/
public int calculate(int left, int right) {
return left - right;
}
}
二、 Exercise习题类与实现
1.设计原则
按照单一职责原则,把类Exercise 的产生职责和使用职责分离出来,使类Exercise
仅仅作为一个存储和管理一定数量的算式题的数据集容器,同时建立一个使用习题
的新类ExerciseSheet。
2.数据结构的选取
– ExerciseSheet无法访问类Exercise中存放算式的私有成员operationList。为了能讥其仑对象使用Exercise 中存储在Array 中的算式,它必项提供公共操作,如检索、遍历等。
– 类Exercise可以选择其仑数据集的数据结构,存放算式。面向对象语言的容器型数据结构,如List、Queue、Stack 等。Java 语言的Collection(C#的ICollection)类局次结构提供了丰富的管理数据集的数据结构、接口和(抽象)类。
策略 1:实现接口。
– 队列Queue 的操作如contains、isEmpty、iterator 等,完全满足案例目前对Exercise 的要求,可以让Exercise 实现接口Queue。
– 在Java中使用队列Queue,除了要实现这4 个方法以外,还必项实现Queue 及其继承的所有其他所有方法,否则只能构造对象实例。
– 这远背了接口隔离原则(InterfaceSegregation Principle,ISP),根据该原则,不应该强迫客户程序依赖于它们不用的方法
策略 2:运用继承。
– 让Exercise 继承容器数据结构中的一个,如具有动态改变容器数量的ArrayList。
– ArrayList可以视为动态数组,即数组大小随需增长。它提供了普通数组的操作
如按下标添加、插入、查询、删除及迭代遍历数据成员的新方法,涵盖了Exercise的设计要求。
但这个设计策略暴露了存储算式的内部数据结构,因而违背了信息隐藏的基本原则。
另外,同接口一样,能选择性地继承操作或属性,子类Exercise 继承了一些不需要的操作。
策略 3:封装结构。
– Exercise 定义容器类数据结构(如Array、ArrayList、Queue、List)为私有成员变量封装,Exercise 提供访问的next、
hasNext 等方法以便ExerciseSheet能够实现遍历等操作。不同的应用需要Exercise 提供的操作可能不完全一样。例如,
目前不需要从练习中删除运算题,就可以不实现删除操作。而且,不同的容器类数据结构,如Array 和ArrayList,对这
些操作的实现也不同。ArrayList 能提供包含上述要求的操作,甚至更多、更方便。
3.UML图
最终实现的类关系图:
4.代码实现
① Exercise习题类
import java.util.ArrayList;
import java.util.Random;
/**
* 习题类Exercise
* 策略3:让Exercise类里包含ArrayList,提供next()和hasNext()公共方法,
* 让客户能够遍历ArrayList里面的元素
* @author lenovo
*
*/
public class Exercise {
// 存放算式的动态数组
private ArrayList<BinaryOperation> operationList = new ArrayList<BinaryOperation>();
private int current = 0; // 动态数组的游标
/**
* 产生加法算式习题
* @param operationCount:习题中算式的个数
*/
public void generateAdditionExercise(int operationCount) {
BinaryOperation anOperation;
while(operationCount > 0) {
do {
anOperation = new AdditionOperation();
}while(operationList.contains(anOperation));
operationList.add(anOperation);
operationCount--;
}
}
/**
* 产生减法算式习题
* @param operationCount:习题中算式的个数
*/
public void generateSubstractExercise(int operationCount) {
BinaryOperation anOperation;
while(operationCount > 0) {
do {
anOperation = new SubstractOperation();
}while(operationList.contains(anOperation));
operationList.add(anOperation);
operationCount--;
}
}
/**
* 产生加减法混合算式习题
* @param operationCount:习题中算式的个数
*/
public void generateBinaryExercise(int operationCount) {
BinaryOperation anOperation;
Random random = new Random();
while(operationCount > 0) {
do {
int opValue = random.nextInt(2);
if(opValue == 0)
anOperation = new AdditionOperation();
else
anOperation = new SubstractOperation();
}while(operationList.contains(anOperation));
operationList.add(anOperation);
operationCount--;
}
}
/**
* 遍历判断是否有下一个算式
* @return
*/
public boolean hasNext() {
return current <= operationList.size()-1;
}
/**
* 遍历返回当前算式,游标+1
* @return 当前算式
*/
public BinaryOperation next() {
return operationList.get(current++);
}
}
② ExerciseSheet类,使用习题
/**
* ExerciseSheet使用(打印显示)习题类
* @author lenovo
*
*/
public class ExerciseSheet {
private static final short COLUMN_NUM = 5;
/**
* 格式化输出
* @param ex:习题类对象
* @param columns:每行算式的个数
*/
public void formattedDisplay(Exercise ex, int columns) {
int column =1;
int count = 1;
while(ex.hasNext()) {
if(column > columns) {
System.out.println();
column = 1;
}
System.out.printf("%3d. ", count);
System.out.print((ex.next()).asString() + "\t");
column++;
count++;
}
System.out.println();
}
public void formattedDisplay(Exercise ex) {
formattedDisplay(ex, COLUMN_NUM);
}
public static void main(String[] args) {
ExerciseSheet sheet = new ExerciseSheet();
Exercise exerciseAdd = new Exercise();
Exercise exerciseSub = new Exercise();
Exercise exerciseMix = new Exercise();
/*产生加法算式习题*/
exerciseAdd.generateAdditionExercise(20);
exerciseAdd.generateAdditionExercise(40);
System.out.println("---- 显示加法算式习题 ----");
sheet.formattedDisplay(exerciseAdd, 4);
/*产生减法算式习题*/
System.out.println("---- 显示减法算式习题 ----");
exerciseSub.generateSubstractExercise(20);
sheet.formattedDisplay(exerciseSub, 4);
/*产生加、减法混合算式习题*/
System.out.println("---- 显示加减法混合算式习题 ----");
exerciseMix.generateBinaryExercise(20);
sheet.formattedDisplay(exerciseMix, 4);
}
}
运行结果如下