原型设计模式—解决随机乱序出试卷(试题顺序、选项顺序随机打乱)

1、实现机制

 

  • 实现机制:

 
       通过实现 Cloneable接口,再重写 clone()方法返回想要的对象。原型模式实现clone接口的时候必须使用深拷贝。
 
       原型模式实际上就是从一个对象再创建另外一个可定制的对象,且无需知道任何创建的细节。在初始化的信息不发生变化的情况下,克隆是最好的办法,既隐藏了对象创建的细节,又大大提高了性能。因为如果不用 clone,每次 new 都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行初始化操作就太低效了。
 

  • 优点:
  • 使用原型模式创建对象比直接new一个对象,在性能上优势明显。因为 Object 类的 clone() 方法是一个本地方法,直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
  • 使用原型模式可以简化对象的创建,使得创建对象很简单。
  • 逃避构造函数的约束。使用原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。
  • 缺点: 必须实现Cloneable接口。

 

2、使用场景

 

  • 希望以一个对象为原型,创建其他近似的对象的时候。
  • 代码优化场景,该场景在业务中用的比较少。一般结合工厂方法模式使用。
  • 多个调用者使用时,并且需要修改对象中的值,可以考虑原型模式来提供对象。

 

3、使用案例

 

3.1、案例说明

 

以下案例,实现场景为,一个试题库中含选择题和简答题,要求根据每个考生将考题顺序打乱,同时将选择题的选项顺序打乱。

 

3.2、代码结构

在这里插入图片描述

3.3、案例实现

 

3.3.1、实体类

  • 简答题实体类 (EssayQuestion)

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 简答题
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EssayQuestion {


    /**
     * 题干
     */
    private String questionStem;

    /**
     * 答案
     */
    private String answer;


}

  • 选择题实体类(ChoiceQuestion)

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashMap;

/**
 * 选择题
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChoiceQuestion {

    /**
     * 题干
     */
    private String questionStem;

    /**
     * 选项
     */
    private HashMap<String, String> option;

    /**
     * 答案
     */
    private String key;

}

 

3.3.2、工具类

  • OutOfOrderHelper 类

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashMap;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OutOfOrderHelper {

    /**
     * 选项
     */
    private HashMap<String, String> option;

    /**
     * 答案
     */
    private String key;

}

  • 选择题选项乱序工具类(OutOfOrderUtil )

import java.util.*;

/**
 * 乱序工具类
 */
public class OutOfOrderUtil {

    /**
     * 乱序Map元素,记录对应答案key
     * @param option 选项
     * @param key    答案
     * @return OutOfOrderHelper 选项乱序后的对象
     */
    public static OutOfOrderHelper random(Map<String, String> option, String key) {
        // 获取所有选项的key值:(例:A、B、C、D)
        Set<String> keySet = option.keySet();
        ArrayList<String> keyList = new ArrayList<String>(keySet);
        // 使用默认随机源对列表进行置换,所有key置换发生的可能性都是大致相等的。
        Collections.shuffle(keyList);
        HashMap<String, String> newOption = new HashMap<>();
        int idx = 0;
        String newKey = "";
        for (String next : keySet) {
            String randomKey = keyList.get(idx++);
            if (key.equals(next)) {
                newKey = randomKey;
            }
            newOption.put(randomKey, option.get(next));
        }
        return new OutOfOrderHelper(newOption, newKey);
    }

}

 

3.3.3、实现类

  • 实现类(ExamHelper),实现 Cloneable类,重写 clone() 方法

import com.prototype.util.OutOfOrderHelper;
import com.prototype.util.OutOfOrderUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExamHelper implements Cloneable {

    /**
     * 考生姓名
     */
    private String candidate;

    /**
     * 考号
     */
    private String candidateNumber;


    /**
     * 简答题集合
     */
    private ArrayList<EssayQuestion> essayQuestions = new ArrayList<>();


    /**
     * 问答题集合
     */
    private ArrayList<ChoiceQuestion> choiceQuestions = new ArrayList<>();


    public ExamHelper append(EssayQuestion essayQuestion) {
        this.essayQuestions.add(essayQuestion);
        return this;
    }


    public ExamHelper append(ChoiceQuestion choiceQuestion) {
        this.choiceQuestions.add(choiceQuestion);
        return this;
    }

    /**
     * 复制对象
     *
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    public Object clone() throws CloneNotSupportedException {
        ExamHelper examHelper = (ExamHelper) super.clone();
        examHelper.choiceQuestions = (ArrayList<ChoiceQuestion>) this.choiceQuestions.clone();
        examHelper.essayQuestions = (ArrayList<EssayQuestion>) this.essayQuestions.clone();
        // 对题目进行乱序处理
        Collections.shuffle(examHelper.essayQuestions);
        Collections.shuffle(examHelper.choiceQuestions);
        // 对每个选择题的选项进行乱序处理
        ArrayList<ChoiceQuestion> choiceQuestionList = examHelper.choiceQuestions;
        choiceQuestionList.forEach(t -> {
            OutOfOrderHelper outOfOrderHelper = OutOfOrderUtil.random(t.getOption(), t.getKey());
            t.setOption(outOfOrderHelper.getOption());
            t.setKey(outOfOrderHelper.getKey());
        });
        return examHelper;
    }

    @Override
    public String toString() {

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

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

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

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

        return detail.toString();
    }


}

  • ExamMaker 类

import java.util.HashMap;

public class ExamMaker {

    ExamHelper examHelper = new ExamHelper();


    public ExamMaker() {


        HashMap<String, String> option1 = new HashMap<>();
        option1.put("A", "继承自Throwable");
        option1.put("B", "Serialable");
        option1.put("C", "不记得,反正不正确");
        option1.put("D", "Run");


        HashMap<String, String> option2 = new HashMap<>();
        option2.put("A", "class中的constructor不可省略");
        option2.put("B", "constructor必须与class同名,但方法不能与class同名");
        option2.put("C", "constructor在一个对象被new时执行");
        option2.put("D", "一个class只能定义一个constructor");


        examHelper.append(new ChoiceQuestion("下面关于java.lang.Exception类的说法正确的是()", option1, "A"))
                .append(new ChoiceQuestion("下列说法正确的有()", option2, "C"));


        examHelper.append(new EssayQuestion("JDBC操作数据库的步骤?",
                "\n" +
                        "1.加载数据库驱动\n" +
                        "2.建立到数据库的连接\n" +
                        "3.定义sql,获取sql执行环境,设置相应的参数\n" +
                        "4.执行sql,处理sql执行结果——DML语句返回int, DQL语句返回结果集对象 ResultSet\n" +
                        "5.释放资源"))
                .append(new EssayQuestion("String,StringBuilder,StringBuffer的区别",
                        "\n" +
                                "1、 String是定长字符串;StringBuilder,StringBuffer是变长字符串\n" +
                                "2、 StringBuilder是线程非安全,一般用于单线程中,执行效率较StringBuffer高,StringBuffer是线程安全的,如果在多个线程中需要同步则采用此类"))
                .append(new EssayQuestion("List和Map的区别",
                        "\n" +
                                "List:是存储单列数据的集合,存储的数据有序且可重复\n" +
                                "Map: 是存储双列数据的集合,采用键值对的形式进行存储,存储的数据是无序的,且key不能重复,但是value值可以重复"));
    }

    /**
     * 创建试卷
     * @param candidate
     * @param candidateNumber
     * @return
     * @throws CloneNotSupportedException
     */
    public String createExam(String candidate, String candidateNumber) throws CloneNotSupportedException {
        ExamHelper examHelperClone = (ExamHelper) examHelper.clone();
        examHelperClone.setCandidate(candidate);
        examHelperClone.setCandidateNumber(candidateNumber);
        return examHelperClone.toString();
    }


}

 

3.3.4、测试类

 


public class ExamTest {

    public static void main(String[] args) {

        ExamMaker examMaker = new ExamMaker();
        try {
            System.out.println(examMaker.createExam("张三", "9527"));
            System.out.println(examMaker.createExam("李四", "9528"));
            System.out.println(examMaker.createExam("王五", "9529"));
            System.out.println(examMaker.createExam("陈六", "9530"));
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        
    }

}

 

3.3.5、测试结果

 


考生:张三
考号:9527
--------------------------------------------
一、选择题

第1题:下列说法正确的有()
A:一个class只能定义一个constructor
Bclass中的constructor不可省略
C:constructor必须与class同名,但方法不能与class同名
D:constructor在一个对象被new时执行
答案:D2题:下面关于java.lang.Exception类的说法正确的是()
A:继承自Throwable
BRun
C:不记得,反正不正确
DSerialable
答案:A

二、简答题

第1题:StringStringBuilderStringBuffer的区别
答案:
1String是定长字符串;StringBuilderStringBuffer是变长字符串
2StringBuilder是线程非安全,一般用于单线程中,执行效率较StringBuffer高,StringBuffer是线程安全的,如果在多个线程中需要同步则采用此类

第2题:ListMap的区别
答案:
List:是存储单列数据的集合,存储的数据有序且可重复
Map: 是存储双列数据的集合,采用键值对的形式进行存储,存储的数据是无序的,且key不能重复,但是value值可以重复

第3题:JDBC操作数据库的步骤?
答案:
1.加载数据库驱动
2.建立到数据库的连接
3.定义sql,获取sql执行环境,设置相应的参数
4.执行sql,处理sql执行结果——DML语句返回int, DQL语句返回结果集对象 ResultSet
5.释放资源


考生:李四
考号:9528
--------------------------------------------
一、选择题

第1题:下列说法正确的有()
A:constructor在一个对象被new时执行
Bclass中的constructor不可省略
C:constructor必须与class同名,但方法不能与class同名
D:一个class只能定义一个constructor
答案:A2题:下面关于java.lang.Exception类的说法正确的是()
ARun
B:继承自Throwable
CSerialable
D:不记得,反正不正确
答案:B

二、简答题

第1题:ListMap的区别
答案:
List:是存储单列数据的集合,存储的数据有序且可重复
Map: 是存储双列数据的集合,采用键值对的形式进行存储,存储的数据是无序的,且key不能重复,但是value值可以重复

第2题:JDBC操作数据库的步骤?
答案:
1.加载数据库驱动
2.建立到数据库的连接
3.定义sql,获取sql执行环境,设置相应的参数
4.执行sql,处理sql执行结果——DML语句返回int, DQL语句返回结果集对象 ResultSet
5.释放资源

第3题:StringStringBuilderStringBuffer的区别
答案:
1String是定长字符串;StringBuilderStringBuffer是变长字符串
2StringBuilder是线程非安全,一般用于单线程中,执行效率较StringBuffer高,StringBuffer是线程安全的,如果在多个线程中需要同步则采用此类


考生:王五
考号:9529
--------------------------------------------
一、选择题

第1题:下面关于java.lang.Exception类的说法正确的是()
ARun
B:继承自Throwable
CSerialable
D:不记得,反正不正确
答案:B2题:下列说法正确的有()
Aclass中的constructor不可省略
B:constructor必须与class同名,但方法不能与class同名
C:一个class只能定义一个constructor
D:constructor在一个对象被new时执行
答案:D

二、简答题

第1题:JDBC操作数据库的步骤?
答案:
1.加载数据库驱动
2.建立到数据库的连接
3.定义sql,获取sql执行环境,设置相应的参数
4.执行sql,处理sql执行结果——DML语句返回int, DQL语句返回结果集对象 ResultSet
5.释放资源

第2题:StringStringBuilderStringBuffer的区别
答案:
1String是定长字符串;StringBuilderStringBuffer是变长字符串
2StringBuilder是线程非安全,一般用于单线程中,执行效率较StringBuffer高,StringBuffer是线程安全的,如果在多个线程中需要同步则采用此类

第3题:ListMap的区别
答案:
List:是存储单列数据的集合,存储的数据有序且可重复
Map: 是存储双列数据的集合,采用键值对的形式进行存储,存储的数据是无序的,且key不能重复,但是value值可以重复


考生:陈六
考号:9530
--------------------------------------------
一、选择题

第1题:下面关于java.lang.Exception类的说法正确的是()
A:继承自Throwable
BRun
CSerialable
D:不记得,反正不正确
答案:A2题:下列说法正确的有()
Aclass中的constructor不可省略
B:constructor必须与class同名,但方法不能与class同名
C:constructor在一个对象被new时执行
D:一个class只能定义一个constructor
答案:C

二、简答题

第1题:JDBC操作数据库的步骤?
答案:
1.加载数据库驱动
2.建立到数据库的连接
3.定义sql,获取sql执行环境,设置相应的参数
4.执行sql,处理sql执行结果——DML语句返回int, DQL语句返回结果集对象 ResultSet
5.释放资源

第2题:StringStringBuilderStringBuffer的区别
答案:
1String是定长字符串;StringBuilderStringBuffer是变长字符串
2StringBuilder是线程非安全,一般用于单线程中,执行效率较StringBuffer高,StringBuffer是线程安全的,如果在多个线程中需要同步则采用此类

第3题:ListMap的区别
答案:
List:是存储单列数据的集合,存储的数据有序且可重复
Map: 是存储双列数据的集合,采用键值对的形式进行存储,存储的数据是无序的,且key不能重复,但是value值可以重复


 
 
 
 
 
 
 
 
.

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值