第十二周_S-设计模式之原型模式

81 阅读4分钟

概念

原型模式(prototype): 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。「解决创建重复对象问题,而这部分对象本身比较复杂,因此采用克隆的方式节省时间」。

大话设计模式:原型模式就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。

案例分析

考试试卷,一般在外考试,试题和答案都会打乱,但是整个试卷的内容是一样的。既满足:给不同的用户创建不同对象。如果这些题目都是从外部,比如 RPC 接口拿到,那是非常耗时的。这时候,我们就可以拿一次数据,然后其他的都复制即可。

直接实现

问答题类:

public class AnswerQuestion {
      private String name;
      private String key;

      public AnswerQuestion(){

      }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

}

选择题类:

public class ChoiceQuestion {
     private String name;    //题目
     private Map<String,String> option;  //选项
     private String key;     //答案

    public ChoiceQuestion(){

    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Map<String, String> getOption() {
        return option;
    }

    public void setOption(Map<String, String> option) {
        this.option = option;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}

一把梭哈实现:创建题目,输出结果

public class QuestionBankController {

    public String createPaper(String candidate, String number) {
        List<ChoiceQuestion> choiceQuestionList = new ArrayList<ChoiceQuestion>();
        List<AnswerQuestion> answerQuestionList = new ArrayList<AnswerQuestion>();

        Map<String, String> map01 = new HashMap<String, String>();
        map01.put("A", "JAVA EE");
        map01.put("B", "JAVA Card");
        map01.put("C", "JAVA ME");
        map01.put("D", "JAVA HE");
        map01.put("E", "JAVA SE");

        Map<String, String> map02 = new HashMap<String, String>();
        map02.put("A", "JAVA程序的main⽅方法必须写在类⾥面");
        map02.put("B", "JAVA程序中可以有多个main⽅方法");
        map02.put("C", "JAVA程序中类名必须与⽂文件名⼀一样");
        map02.put("D", "JAVA程序的main⽅方法中如果只有⼀一条语句句,可以不不⽤用{}(⼤大括号)括\n" +
                "起来");

        Map<String, String> map03 = new HashMap<String, String>();
        map03.put("A", "变量量由字⺟母、下划线、数字、 $符号随意组成;");
        map03.put("B", "变量量不不能以数字作为开头");
        map03.put("C", "A和a在java中是同⼀一个变量量");
        map03.put("D", "不同类型的变量量,可以起相同的名字");

        Map<String, String> map04 = new HashMap<String, String>();
        map04.put("A", "STRING");
        map04.put("B", "x3x;");
        map04.put("C", "void");
        map04.put("D", "de$f");

        choiceQuestionList.add(new ChoiceQuestion("JAVA所定义的版本中不包括", map01, "D"));
        choiceQuestionList.add(new ChoiceQuestion("下列说法正确的说法是", map02, "A"));
        choiceQuestionList.add(new ChoiceQuestion("变量量命名规范说法正确的是", map03, "B"));
        choiceQuestionList.add(new ChoiceQuestion("以下()不不是合法的标识符", map04, "C"));

        answerQuestionList.add(new AnswerQuestion("⼩小红⻢马和⼩小⿊黑⻢马⽣生的⼩小⻢马⼏几条腿", "4条腿"));
        answerQuestionList.add(new AnswerQuestion("铁棒打头疼还是⽊木棒打头疼", "头最疼"));
        answerQuestionList.add(new AnswerQuestion("什什么床不不能睡觉", "⽛床"));
        answerQuestionList.add(new AnswerQuestion("为什什么好⻢马不不吃回头草", "后⾯面的草没了了"));

        // 输出结果
        StringBuilder detail = new StringBuilder("考⽣生: " + candidate +
                "\r\n" +
                "考号: " + number + "\r\n" +
                "--------------------------------------------\r\n" +
                "⼀一、选择题" + "\r\n\n");

        for (int idx = 0; idx < choiceQuestionList.size(); idx++) {
            detail.append("第").append(idx + 1).append("题: ").append(choiceQuestionList.get(idx).getName()).append("\r\n");
            Map<String, String> option = choiceQuestionList.get(idx).getOption();
            for (String key : option.keySet()) {
                detail.append(key).append(": ").append(option.get(key)).append("\r\n");
            }
            detail.append("答案: ").append(choiceQuestionList.get(idx).getKey()).append("\r\n\n");
        }

        detail.append("二、问答题" + "\r\n\n");
        for (int idx = 0; idx < answerQuestionList.size(); idx++) {
            detail.append("第").append(idx + 1).append("题: ").append(answerQuestionList.get(idx).getName()).append("\r\n");
            detail.append("答案: ").append(answerQuestionList.get(idx).getKey()).append("\r\n\n");
        }
        return detail.toString();
    }

}

测试类:

public class TestController {
    public static void main(String[] args) {
        QuestionBankController questionBankController =new QuestionBankController();
        System.out.println(questionBankController.createPaper("狸花","123"));
        System.out.println(questionBankController.createPaper("小狸花","1123"));
        System.out.println(questionBankController.createPaper("微小狸花","11123"));
    }
}

这样的代码非常易于理解,但是没有面向对象,也不易于扩展。

原型模式

克隆对象处理

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;
    }

    public void setCandidate(String candidate) {
        this.candidate = candidate;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String toString() {

        StringBuilder detail = new StringBuilder("考生:" + candidate + "\r\n" +
                "考号:" + number + "\r\n" +
                "--------------------------------------------\r\n" +
                "一、选择题" + "\r\n\n");

        for (int idx = 0; idx < choiceQuestionList.size(); idx++) {
            detail.append("第").append(idx + 1).append("题:").append(choiceQuestionList.get(idx).getName()).append("\r\n");
            Map<String, String> option = choiceQuestionList.get(idx).getOption();
            for (String key : option.keySet()) {
                detail.append(key).append(":").append(option.get(key)).append("\r\n");;
            }
            detail.append("答案:").append(choiceQuestionList.get(idx).getKey()).append("\r\n\n");
        }

        detail.append("二、问答题" + "\r\n\n");

        for (int idx = 0; idx < answerQuestionList.size(); idx++) {
            detail.append("第").append(idx + 1).append("题:").append(answerQuestionList.get(idx).getName()).append("\r\n");
            detail.append("答案:").append(answerQuestionList.get(idx).getKey()).append("\r\n\n");
        }

        return detail.toString();
    }

}
public class Topic {

    private Map<String, String> option;  // 选项;A、B、C、D
    private String key;           // 答案;B

    public Topic() {
    }

    public Topic(Map<String, String> option, String key) {
        this.option = option;
        this.key = key;
    }

    public Map<String, String> getOption() {
        return option;
    }

    public void setOption(Map<String, String> option) {
        this.option = option;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}

乱序工具包

public class TopicRandomUtil {

    /**
     * 乱序Map元素,记录对应答案key
     * @param option 题目
     * @param key    答案
     * @return Topic 乱序后 {A=c., B=d., C=a., D=b.}
     */
    static public Topic random(Map<String, String> option, 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 QuestionBankController {

    private QuestionBank questionBank = new QuestionBank();

    public QuestionBankController() {

        Map<String, String> map01 = new HashMap<String, String>();
        map01.put("A", "JAVA2 EE");
        map01.put("B", "JAVA2 Card");
        map01.put("C", "JAVA2 ME");
        map01.put("D", "JAVA2 HE");
        map01.put("E", "JAVA2 SE");

        Map<String, String> map02 = new HashMap<String, String>();
        map02.put("A", "JAVA程序的main方法必须写在类里面");
        map02.put("B", "JAVA程序中可以有多个main方法");
        map02.put("C", "JAVA程序中类名必须与文件名一样");
        map02.put("D", "JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来");

        Map<String, String> map03 = new HashMap<String, String>();
        map03.put("A", "变量由字母、下划线、数字、$符号随意组成;");
        map03.put("B", "变量不能以数字作为开头;");
        map03.put("C", "A和a在java中是同一个变量;");
        map03.put("D", "不同类型的变量,可以起相同的名字;");

        Map<String, String> map04 = new HashMap<String, String>();
        map04.put("A", "STRING");
        map04.put("B", "x3x;");
        map04.put("C", "void");
        map04.put("D", "de$f");


        questionBank.append(new ChoiceQuestion("JAVA所定义的版本中不包括", map01, "D"))
                .append(new ChoiceQuestion("下列说法正确的是", map02, "A"))
                .append(new ChoiceQuestion("变量命名规范说法正确的是", map03, "B"))
                .append(new ChoiceQuestion("以下()不是合法的标识符",map04, "C"))
                .append(new AnswerQuestion("小红马和小黑马生的小马几条腿", "4条腿"))
                .append(new AnswerQuestion("铁棒打头疼还是木棒打头疼", "头最疼"))
                .append(new AnswerQuestion("什么床不能睡觉", "牙床"))
                .append(new AnswerQuestion("为什么好马不吃回头草", "后面的草没了"));
    }

    public String createPaper(String candidate, String number) throws CloneNotSupportedException {
        QuestionBank questionBankClone = (QuestionBank) questionBank.clone();
        questionBankClone.setCandidate(candidate);
        questionBankClone.setNumber(number);
        return questionBankClone.toString();
    }

}

一份数据,其他都是克隆。

总结

整个模式使用率不高。不要过度设计

如果上面让我来做,我能想到的就是只拿一份数据,然后创建新对象。而不会想到克隆。实际开发中,确实也没用到过 clone