在一个简单答题系统的设计与实现(一)博文中列出了需求分析,与详细设计,现在按照之前的详细设计开始一步一步实现。

文件设计(数据层设计)

该系统由于需求场景限制,在数据层利用文件和文件的读写实现查询。具体思路如下图:

android答题单选多选 android答题系统设计_业务设计


在该系统中用到的是以txt(文本文档)文件作为应用程序访问目标。既然是数据层,那么基础的CRUD肯定是要实现的。在该系统的实现中,我将其定位成工具类,功能是加载文件。我们在实现功能时,一定要想到这个功能不单单只能在我的程序中用到,别人如果需要用到这种功能时,拿我已经实现的功能直接可以用,即可复用性强。由于使用文件对数据要求繁琐复杂,必须将数据先转化成能够直接读取格式。

FileUtil.java 代码:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import com.contest.model.Question;

/*读取文件内容*/
public class FileUtil {
	/*将文件中的内容读取后按分割符分割并且加入列表返回*/
	public static List<String[]> readTxtToParameter(String fileName){
		File file = new File(fileName);
		List<String[]> resultSet = new ArrayList<String []>();
		String [] results = {};
		try{
			BufferedReader br = new BufferedReader(new FileReader(file));
			String result = null;
			while((result = br.readLine()) != null){
				results = result.split("@");
				resultSet.add(results);
			}
		}catch(Exception e){
			System.err.println("读取文件"+fileName+"失败");
			e.printStackTrace();
		}
		return resultSet;
	}
	
	//将指定txt文本写入路径名为fileName的文件中
	public static void writeTxt(String fileName,String txt){
        File writename = new File(fileName);
        try {
			writename.createNewFile();
			BufferedWriter out = new BufferedWriter(new FileWriter(writename)); 
	        out.write(txt); // \r\n即为换行 
	        out.flush(); // 把缓存区内容压入文件 
	        out.close(); // 最后记得关闭文件 
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//测试
	public static void main(String args[]){
		System.out.println(readTxtToParameter("data/test.txt").get(0)[3]);
		writeTxt("data/test1.txt", "@20");
		System.out.println(readTxtToParameter("data/test1.txt").get(0)[1]);
	}
}

当然,这两个方法现在只是实现打标(之前说的回到上次答题位置功能)和加载,将题库先缓存在系统中。在实现至其他功能时,会在这里添加它该做的事情(方法)。

业务设计(逻辑设计)

在设计程序时,那么该应用程序一定是为了解决某种问题(或者能够使原本的事情能够更加便捷高效的完成)。解决问题肯定是有解决方法(解决步骤)的,这解决方法就是我们设计程序时所说的逻辑。在简单答题系统中,答题的逻辑就是:

  • 答的是什么题
  • 答的题是否正确
  • 答错的题正确答案是什么?

当然,按照之前的UI设计,在该简单答题系统中还有其他的逻辑,比如:

  • 上一题,下一题
  • 生成试卷等

当我们能够理清自己设计系统中需要的逻辑时,在之后的代码设计中,能够减少一些”坏味道“(强耦合)。
在离用户最近的用户界面展示信息时,用户所看到的信息是怎样传过去的呢,是通过在数据库中直接查找的(或者是本系统中从文件中)?答案肯定是不对的,向上面那种做法,最终的代码会很不可读,创建连接和增删改查的语句遍布,让人难以理解其中的含义,更别说维护了。所以,我们会将业务信息抽象成一个类,即业务类,该类创建的对象即业务对象,也就是我们经常说的Bean。并且,将业务逻辑也整合成类,那么它创建的对象即业务逻辑对象,当然在本系统中不止一个业务逻辑类(每个业务逻辑干自己该干的事情)。下面是在本系统中按照之前的需求分析设计的业务类:

public class Question {
    public static final String CHOOSE_QUESTION = "1";/*选择题*/
    public static final String BLANK_QUESTION = "2";/*填空题*/
    public static final String JUDGE_QUESTION = "3";/*判断题*/
    public static final String JD_QUESTION = "4";/*简答题*/
    private String questionId;/*问题编号*/
    private String questionDesc;/*问题描述*/
    private String answerDesc;/*答案描述*/
    private String questionType;/*问题类型*/
    private String remark;/*备注*/


    public Question(String questionId,String questionDesc,String answerDesc,String questionType){
        this.questionId = questionId;
        this.questionDesc = questionDesc;
        this.answerDesc = answerDesc;
        this.questionType = questionType;
    }

    public String getQuestionId() {
        return questionId;
    }

    public void setQuestionId(String questionId) {
        this.questionId = questionId;
    }

    public String getQuestionDesc() {
        return questionDesc;
    }

    public void setQuestionDesc(String questionDesc) {
        this.questionDesc = questionDesc;
    }

    public String getAnswerDesc() {
        return answerDesc;
    }

    public void setAnswerDesc(String answerDesc) {
        this.answerDesc = answerDesc;
    }

    public String getQuestionType() {
        return questionType;
    }

    public void setQuestionType(String questionType) {
        this.questionType = questionType;
    }


    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Question question = (Question) o;

        if (questionDesc != null ? !questionDesc.equals(question.questionDesc) : question.questionDesc != null)
            return false;
        return questionType != null ? questionType.equals(question.questionType) : question.questionType == null;
    }

    @Override
    public int hashCode() {
        int result = questionDesc != null ? questionDesc.hashCode() : 0;
        result = 31 * result + (questionType != null ? questionType.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Question [questionId=" + questionId + ", questionDesc=" + questionDesc + ", answerDesc=" + answerDesc
                + ", questionType=" + questionType + ", remark=" + remark + "]";
    }
}

在该类中有三个值得注意的地方:

  • 定义的常量
  • equals与hashCode方法
  • toString方法

为什么要这样做?

  • 定义常量是为了将题的类型代码统一,在其他类中使用时直接调用,见名知意,且在之后维护时修改方便。
  • 重写equals与hashCode方法,是为了在判断两个题是否同一题。以题的描述与题型判断。
  • 重写toString方法是在本系统为了方便向文件中写入自定义格式化的对象。

在下一篇博客中,开始设计与实现各个部分业务逻辑。博主水平有限,希望与大家一起进步,欢迎大家指正。