创建型-原型模式 Link to heading

原型模式主要解决的问题就是创建重复对象,而这部分对象内容本身比较复杂,生成过程可能从库或者RPC接口中获取数据的耗时较长,因此采用克隆的方式节省时间。

这种场景就像CV代码、java中的clone一样。

模拟场景 Link to heading

一个上机考试抽题服务,做到能够混排题目,混排答案。这个服务用于选择题、问答题的试卷创建。

数据准备

public class ChoiceQuestion{
  private String name;
  private Map<String, String> option;
  private String key;
  public ChoiceQuestion(String name. Map<String, String> option, String key){
    this.name = name;
    this. option = option;
    this. key = key;
  }
}

public class AnswerQuestion{
  private String name;
  private String key;
  public AnswerQuestion(String name,String key){
    this.name = name;
    this.key = key;
  }
}

最初设计 Link to heading

public class QuestionBankController{
  public String createPaper(Stirng candidate, String number){
    // do sth
    // 手工填写不同问题
  }
}

这样的代码非常容易理解,要什么题目就塞进去,只面向过程,不考虑扩展性。

以上的代码有一个没有实现的地方就是不能乱序,所有人的试卷都是一样的。

改进方案 Link to heading

工程结构

└─design
	├─util
	│ ├─Topic.java
	│ └─TopicRandomUtil.java
	├─QuestionBank.java
	└─QuestionBankController.java
  • 工程中包含了核心的题库类QuestionBank,题库主要负责将各个的题目进行组装最终输出试卷。
  • 针对每一个试卷都会使用克隆的方式进行复制,复制完成后将试卷中的题目以及每个题目的答案进行乱序处理。

乱序工具包

public class TopicRandomUtil{
  static public Topic random(Map<String, String> optionn, String key){
    Set<String> keySet = option.keySet();
    ArrayList<String> keyList = new ArrayList<String>(keySet);
    Collections.shuffle(keyList);
    HashMap<String, String> optionNew = new HashMap<String, String>();
    int idx = 0;
    String keyNew = "";
    for (String next : keySet) {
        String randomKey = keyList.get(idx++);
        if (key.equals(next)) {
            keyNew = randomKey;
        }
        optionNew.put(randomKey, option.get(next));
    }
    return new Topic(optionNew, keyNew);
  }
}

克隆对象处理类

public class QuestionBank implements Cloneable{
  private String candidate;
  private String number;
  private ArrayList<ChoiceQuestion> choiceQuestionList = new ArrayList<ChoiceQuestion>();
    private ArrayList<AnswerQuestion> answerQuestionList = new
ArrayList<AnswerQuestion>();
  
  // 添加题目
  public QuestionBank append(ChoiceQuestion choiceQuestion) {
        choiceQuestionList.add(choiceQuestion);
        return this;
	}
  public QuestionBank append(AnswerQuestion answerQuestion) {
        answerQuestionList.add(answerQuestion);
        return this;
	}
  
  
  @Override
  public Object clone() throws CLoneNotSupportedException{
    QuestionBank questionBank = (QuestionBank) super.clone();
        questionBank.choiceQuestionList = (ArrayList<ChoiceQuestion>) choiceQuestionList.clone();
        questionBank.answerQuestionList = (ArrayList<AnswerQuestion>) answerQuestionList.clone();
    // 题目乱序
    Collections.shuffle(questionBank.choiceQuestionList); 	
    Collections.shuffle(questionBank.answerQuestionList);
    // 答案乱序
    ArrayList<ChoiceQuestion> choiceQuestionList = questionBank.choiceQuestionList;
    for (ChoiceQuestion question : choiceQuestionList) {
      Topic random = TopicRandomUtil.random(question.getOption(),question.getKey());
      question.setOption(random.getOption());
      question.setKey(random.getKey());
    }
    return questionBank;
  }
}

这里的操作主要三个

  • 两个append()对题目的添加
  • clone(),这里的核心操作就是对对象的复制,这里复制不只包含了本身,同时对两个集合也做了复制。
  • 乱序操作,使用的是lsitCollections.shuffle可以将原有的集合打乱。

初始化试卷数据

public class QuestionBankController{
  private QUestionBank questionBank = new QuestionBank();
  
  public QuestionBankController(){
    // do sth
  }
  
  public String createPaper(String candidate, String number) throws CloneNotSupportedException{
    QuestionBank questionBankClone = (QuestionBank) questionBank.clone();
    // do sth
    return questionBankClone.toString();
  }
}