我试图在Java中实践OO设计和OOP,所以我创建了一个尝试面向对象和可扩展的考试系统。到目前为止,我还没有从Java中学到以下内容: Java 8、接口、集合、IO、Swing、异常
public abstract class Question {
protected int score;
protected boolean isAnswerRight;
public Question(int puntuacion) {
this.score = puntuacion;
}
public int getScore() {
return score;
}
public abstract void ask();
public abstract void showClue();
}这个班有两个孩子
import javax.swing.*;
public class MultipleOptionQuestion extends Question {
private static final int MULTIPLE_OPTION_SCORE = 1;
private String statement;
private String[] options;
private String clue;
private int rightOption;
public MultipleOptionQuestion(int score, String statement, String[] options, int rightOption, String clue) {
super(MULTIPLE_OPTION_SCORE);
this.statement = statement;
this.options = options;
this.clue = clue;
this.rightOption = rightOption;
}
@Override
public void ask() {
String answer = (String) JOptionPane.showInputDialog(null, statement, "Question Test", JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
if (answer.equals(options[rightOption])) {
this.isAnswerRight = true;
}
}
@Override
public void showClue() {
JOptionPane.showMessageDialog(null, clue);
}
}和一个抽象类来表示简单的数学操作(+,-,*,/)
import javax.swing.*;
public abstract class ArithmeticQuestion extends Question {
//additions may have a wider interval in order to make them harder
// than multiplications, for example, and we want divisions to be exact so
//child classes provide operands
protected int first;
protected int second;
protected int result;
public int getFirstOperand() {
return first;
}
public int getSecondOperand() {
return second;
}
public int getResult() {
return result;
}
public abstract char getOperator();
public ArithmeticQuestion(int score) {
super(score);
}
public String stringQuestion() {
return "How much is it? " +
getFirstOperand() +
getOperator() +
getSecondOperand();
}
@Override
public void ask() {
String answer = null;
while (answer == null || answer.equals("")) {
answer = JOptionPane.showInputDialog(null, this.stringQuestion());
if (answer != null) {
if (Integer.parseInt(answer) == getResult()) {
isAnswerRight = true;
}
}
}
System.out.println(isAnswerRight ? "Right" : "Wrong"); //For debugging
}
}这里我不确定我是否做了一个好的设计(我不知道我是否有太多的课程)
import javax.swing.*;
public class AdditionQuestion extends ArithmeticQuestion {
private static final int ADDITION_MINIMUM = 100;
private static final int ADDITION_INTERVAL = 200;
private static final int ADDITION_SCORE = 1;
public AdditionQuestion() {
super(ADDITION_SCORE);
this.first = (int) (Math.random() * ADDITION_INTERVAL) + ADDITION_MINIMUM;
this.second = (int) (Math.random() * ADDITION_INTERVAL) + ADDITION_MINIMUM;
this.result = first + second;
}
@Override
public char getOperator() {
return '+';
}
@Override
public void showClue() {
JOptionPane.showMessageDialog(null, "Be careful when you add more than 10 units");
}
}减法类
import javax.swing.*;
public class SubtractionQuestion extends ArithmeticQuestion {
private static final int SUBTRACTION_MINIMUM = 0;
private static final int SUBTRACTION_INTERVAL = 100;
private static final int SUBTRACTION_SCORE = 1;
public SubtractionQuestion() {
super(SUBTRACTION_SCORE);
this.first = (int) (Math.random() * SUBTRACTION_INTERVAL) + SUBTRACTION_MINIMUM;
this.second = (int) (Math.random() * SUBTRACTION_INTERVAL) + SUBTRACTION_MINIMUM;
this.result = first - second;
}
@Override
public char getOperator() {
return '-';
}
@Override
public void showClue() {
JOptionPane.showMessageDialog(null, "The answer may be a negative number");
}
}用于乘法
import javax.swing.*;
public class MultiplicationQuestion extends ArithmeticQuestion {
private static final int MULTIPLICATION_MINIMUM = 1;
private static final int MULTIPLICATION_INTERVAL = 10;
private static final int MULTIPLICATION_SCORE = 3;
public MultiplicationQuestion() {
super(MULTIPLICATION_SCORE);
this.first = (int) (Math.random() * MULTIPLICATION_INTERVAL) + MULTIPLICATION_MINIMUM;
this.second = (int) (Math.random() * MULTIPLICATION_INTERVAL) + MULTIPLICATION_MINIMUM;
this.result = first * second;
}
@Override
public char getOperator() {
return '*';
}
@Override
public void showClue() {
String output = "RECALL THE MULTIPLICATION TABLE\n";
for (int i = 1; i < 10; i++) {
output += i + "x" + getFirstOperand() + "=" + i * getSecondOperand() + "\n";
}
JOptionPane.showMessageDialog(null, output);
}
}最后,考试:
import javax.swing.*;
public abstract class Exam {
protected Question[] questions = new Question[50];
protected int currentNumberOfQuestions = 0;
protected int totalScore = 0;
public void addQuestion(Question p) {
questions[currentNumberOfQuestions++] = p;
}
public void increaseScore(int score) {
this.totalScore += score;
}
public int maximumPossibleScore() {
int total = 0;
for (int i = 0; i < currentNumberOfQuestions; i++) {
total += questions[i].getScore();
}
return total;
}
public void examResult() {
JOptionPane.showMessageDialog(null, "You've got " + this.totalScore + " points out of: " + maximumPossibleScore());
}
public abstract void doExam();
}模拟考试(做同样的问题直到你是对的)
public class mockExam extends Exam {
//The student only scores if they are right in their first attempt
//If they are wrong, a clue is shown
@Override
public void doExam() {
for (int i = 0; i < this.currentNumberOfQuestions; i++) {
Question p = this.questions[i];
p.ask();
if (p.isAnswerRight) {
this.increaseScore(p.score);
}
while (!p.isAnswerRight) {
p.showClue();
p.ask();
}
}
examResult();
}
}一次真正的考试
public class RealExamen extends Exam {
//Only asks each question once
@Override
public void doExam() {
for (int i = 0; i < this.currentNumberOfQuestions; i++) {
Question p = this.questions[i];
p.ask();
if (p.isAnswerRight) {
this.increaseScore(p.score);
}
}
examResult();
}
}希望听到您对我如何改进和管理代码的反馈。
发布于 2018-01-24 22:20:58
谢谢你分享你的代码。
OOP的最终目标是减少代码重复、提高可读性和支持重用以及扩展代码。
执行OOP意味着您遵循某些原则(除其他外):
这是一个主要原则(不仅仅是在面向对象程序设计中)。在OOP中,这意味着没有其他类(甚至子类)知道某个类的内部结构。
您违反了这一原则,将Question的子类直接访问其成员变量score和isAnswerRight。
这也违反了说,不要问!原则。
更好的方法是在类Question中添加一个方法来管理score值本身:
public abstract class Question {
private final /*hopefully the score never change during runtime */
int score;
public Question(int puntuacion) {
this.score = puntuacion;
}
/** public entry point, do not override */
public final int ask(){
boolean isAnsweredRight = askUser();
if(!isAnsweredRight) {// may fail once
showClue();
isAnsweredRight = askUser();
}
return isAnsweredRight? score : 0; // no score if failed
};
/** ask the question and report success/failure */
protected abstract boolean askUser();
public abstract void showClue();
}正如注释中提到的那样,Question的一些子类引起了人们的怀疑:
在OOP中,当我们需要改变行为时,我们创建新的(子)类。也就是说:我们重写了一个超类的方法来做一些不同的或额外的计算。(返回值并不是计算.)
我在评论中提出的理由是:一个糟糕的设计是在其他地方完成的,这不应该成为这样做的借口。
因此,我将只有两个Question子类:
public class MultipleOptionQuestion extends Question {和
public class ArithmeticQuestion extends Question {我将介绍另一个接口Operation:
interface Operation{
int calculate(int first, int second);
}ArithmeticQuestion看起来会是这样的:
public class ArithmeticQuestion extends Question {
private final int first;
private final int second;
private final Operation operation;
private final String operator;
private final String clue;
public ArithmeticQuestion(String operator, Operation operation, in first, int second, int score, String clue)
super(score);
// constructors do no work, they just assign values to members
this.operation=operation;
this.operator=operator;
this.first=first;
this.second=second;
this.clue=clue;
}
public String stringQuestion() {
return "How much is it? " +
first +
operator +
second;
}
public void showClue() {
JOptionPane.showMessageDialog(null, clue);
}
@Override
public boolean askUser() {
int result = operation.calculate(first,second);
String answer = null;
while (answer == null || answer.equals("")) {
answer = JOptionPane.showInputDialog(null, this.stringQuestion());
if (answer != null) {
return Integer.parseInt(answer) == result;
}
}
return false; // maybe marked as unreachable code...
}
}这将导致这个exsam类:
public class Exam {
private static final int MULTIPLE_OPTION_SCORE = 1;
private static final int MULTIPLICATION_MINIMUM = 1;
private static final int MULTIPLICATION_INTERVAL = 10;
private static final int MULTIPLICATION_SCORE = 3;
private static final int SUBTRACTION_MINIMUM = 0;
private static final int SUBTRACTION_INTERVAL = 100;
private static final int SUBTRACTION_SCORE = 1;
private static final int ADDITION_MINIMUM = 100;
private static final int ADDITION_INTERVAL = 200;
private static final int ADDITION_SCORE = 1;
private static final int QUESTION_TYPE_COUNT = 4;
public void doExam(int numberOfQuestions) {
Random random = new Random();
int maxScore =0;
int userScore =0;
for (int i = 0; i < numberOfQuestions; i++) {
int questionType =random.nextInt(QUESTION_TYPE_COUNT);
Question question;
switch(questionType){
case 0: // Addition
question = new ArithmeticQuestion("+",
(first,second)->first+second,
random.nextInt(ADDITION_INTERVAL)+ADDITION_MINIMUM,
random.nextInt(ADDITION_INTERVAL)+ADDITION_MINIMUM,
ADDITION_SCORE,
"Be careful when you add more than 10 units");
maxScore+=ADDITION_SCORE;
break;
case 1: // Subtraction
question = new ArithmeticQuestion("-",
(first,second)->first-second,
random.nextInt(SUBTRACTION_INTERVAL)+SUBTRACTION_MINIMUM,
random.nextInt(SUBTRACTION_INTERVAL)+SUBTRACTION_MINIMUM,
SUBTRACTION_SCORE,
"The answer may be a negative number");
maxScore+=SUBTRACTION_SCORE;
break;
case 2: // Multiplication
// ...
default: // MultiOption
// ...
}
userScore+= question.ask();
}
JOptionPane.showMessageDialog(null, "You reached "+userScore+ " of "+maxScore+" possible points!")
}
}发布于 2018-01-25 17:31:05
我的观点(这肯定与蒂莫西的解释重叠):
我真的不喜欢抽象,我会对待它--就像德语里说的那样--“继母”。您有一个抽象类型Question,一个抽象类型MultipleOptionQuestion,它扩展了Question并覆盖了ask()和showClue(),一个类ArithmeticQuestion扩展了Question并提供了其他方法。以及其他几种覆盖和扩展行为的Question类型。这是,温和地说,有点混乱。除了混乱之外,您不能在没有实际实现的情况下测试抽象类型的逻辑,也不能在没有抽象的情况下测试您的实现,除非您非常严格地分离抽象和实现。这也可能违反Liskov的替代原则,稍后会有更多的内容:
的耦合
您的问题类型与swing类有直接的依赖关系。如果你想改变gui,或者使用另一种技术,可能是应用程序,或者网页,你就必须改变业务逻辑。GUI通常是多层体系结构的顶层,并且调用它下面的层,下面的层一定不知道GUI。
现在,如果您实现上面的“耦合”部分,这一点特别重要。它的原理是:你有一个例程A,A用‘超级型’执行一些东西,你有一个X和Y类,它们是S的一个子类型。如果你必须改变常规A,当你引入一个新的S子类型时,你就违反了Liskov的替代原则。例如,在您的示例中,它是ArithmeticQuestion类型。被编程用于与您的类型Question一起工作的类型不能使用AirthmeticQuestion,因为您需要例如getFirstOperand才能正确工作。因此,接下来您必须做的是,例如,对instanceof类型的Question进行转换。有一些方法可以以"oo“的方式来解决这个问题,但我认为在使用子类型时,这是非常重要的。
Question -> AbstractQuestion)。这常常有帮助。maximumPossibleScore应该与getPrefix一起使用。另外,您可能需要考虑只计算一次。mockExam应该以大写M开头。考虑到任何问题的解决办法,爱因斯坦说:尽可能容易,但不容易。老实说,这就是你的解决方案最缺乏的地方。在这里,对于如何更容易地实现它(当然也是有争议的),有一个普遍的想法:
public class Question {
private String question;
private String answer;
private String[] answers;
private Question(String question, String answer) {
this(question);
this.answer = answer;
}
private Question(String question, String[] answers) {
this(question);
this.answers = answers;
}
private Question(String question) {
this.question = question;
}
public boolean isMultipleChoiceQuestions() {
return answers != null;
}
// ...
public static Question createQuestion(String question, String answer) {
return new Question(question, answer);
}
}
public class ExamGenerator {
public static Exam createEasyExam() {
// create questions ...
return new Exam();
}
private static Question createMultiplicationQuestion(int factor1, int factor2, int product) {
return Question.createQuestion("What is blablabla", "put correct answer here");
}
}希望这会有所帮助:)
慢慢来
https://codereview.stackexchange.com/questions/185892
复制相似问题