概念
原型模式(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