原型模式
原型模式的主要作用就是创建对象,我们需要创建的对象可能是来自数据库,也可能是通过RPC接口来获取,这样我们可以通过克隆来获取对象以节省时间。
原型模式在程序应用中很少,并不像工厂、建造者、代理模式用的那么广泛,但是在我们生活中,例如:自身细胞的有丝分裂,再者工作中Ctrl + C 加上 Ctrl + V 的你,这些都是原型模式。
什么时候用原型模式?例如类初始化消耗资源时,再比如创建对象很麻烦时,总之当创建对象的效率很低的时候,可以考虑使用原型模式。
在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
比如我们在四六级考试的时候,每个人都是不一样的试卷,在同一考场的同学,即使听力的部分是一样的,但是每个选项的顺序也是不一样的。我们可以通过模拟考试,通过相同的题库,但是打乱题目顺序和选项来实现原型模式。
原型模式解决的就是创建大量重复的类,最长用的手段就是克隆,在每个需要克隆的都需要实现Cloneable接口。
插曲:在JavaScript中每个对象都有自己的原型,最高的原型就是Object,每个被继承的原型可以把自己的属性传给下一代,有兴趣的可以了解下,类似于Java中的继承。
模拟上机试卷实现原型模式
试卷选择题
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;
}
// 此处省略get set
}
试卷问答题
public class AnswerQuestion {
private String name; // 问题
private String key; // 答案
public AnswerQuestion() {
}
public AnswerQuestion(String name, String key) {
this.name = name;
this.key = key;
}
// 此处省略get set
}
试卷随机排序工具类
/**
* 乱序Map元素,记录对应答案key
* @param option 题⽬
* @param key 答案
* @return Topic 乱序后的答案
*/
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 index = 0;
String keyNew = "";
for (String next : keySet) {
String randomKey = keyList.get(index++);
if (key.equals(next)) {
keyNew = randomKey;
}
optionNew.put(randomKey, option.get(next));
}
//返回乱序后答案对应的map
return new Topic(optionNew, keyNew);
}
试卷类
package PrototypePattern;
/**
* @Author: jerrold chen
* @Date: 2021/03/31
* @Version 1.0
* @description:
*/
public class Question implements Cloneable {
private String name; // 姓名
private String id; // 考号
private ArrayList<ChoiceQuestion> choiceQuestionList = new
ArrayList<ChoiceQuestion>();
private ArrayList<AnswerQuestion> answerQuestionList = new
ArrayList<AnswerQuestion>();
public Question append(ChoiceQuestion choiceQuestion) {
choiceQuestionList.add(choiceQuestion);
return this;
}
public Question append(AnswerQuestion answerQuestion) {
answerQuestionList.add(answerQuestion);
return this;
}
@Override
public Object clone() throws CloneNotSupportedException {
Question question = (QuestionBank) super.clone();
Question.choiceQuestionList = (ArrayList<ChoiceQuestion>)
choiceQuestionList.clone();
Question.answerQuestionList = (ArrayList<AnswerQuestion>)
answerQuestionList.clone();
// 题⽬乱序
Collections.shuffle(question.choiceQuestionList);
Collections.shuffle(question.answerQuestionList);
// 答案乱序
ArrayList<ChoiceQuestion> choiceQuestionList = question.choiceQuestionList;
for (ChoiceQuestion questions : choiceQuestionList) {
Topic random = TopicRandomUtil.random(questions.getOption(),
questions.getKey());
questions.setOption(random.getOption());
questions.setKey(random.getKey());
}
return question;
}
public void setName(String name) {
this.name = name;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
StringBuilder detail = new StringBuilder("考⽣:" + name +
"\r\n" +
"考号:" + id + "\r\n" +
"--------------------------------------------\r\n" +
"⼀、选择题" + "\r\n\n");
for (int i= 0; i < choiceQuestionList.size(); i++) {
detail.append("第").append(i +
1).append("题:").append(choiceQuestionList.get(i).getName()).append("\r\
n");
Map < String, String > option =
choiceQuestionList.get(i).getOption();
for (String key : option.keySet()) {
detail.append(key).append(":").append(option.get(key)).append("\r\n");
;
}
detail.append("答案:").append(choiceQuestionList.get(i).getKey()).append("\r\n\n");
}
detail.append("⼆、问答题" + "\r\n\n");
for (int i = 0; i < answerQuestionList.size(); i++) {
detail.append("第").append(i +
1).append("题:").append(answerQuestionList.get(i).getName()).append("\r\n");
detail.append("答案:").append(answerQuestionList.get(i).getKey()).append("\r\n\n");
}
return detail.toString();
}
}
考试初始化试卷
public class QuestionHander{
private Question question = new Question();
public QuestionHander(){
Map<String, String> map1 = new HashMap<String, String>();
map1 .put("A", "JAVA");
map1 .put("B", "C++");
map1 .put("C", "Golang");
map1 .put("D", "TypeScript");
Map<String, String> map2= new HashMap<String, String>();
map2.put("A", "封装");
map2.put("B", "继承");
map2.put("C", "多态");
map2.put("D", "妈卖批");
}
question.append(new ChoiceQuestion("以下哪种语言最牛逼",map1,"A"))
.append(new ChoiceQuestion("以下哪个是Java没有的特性",map1,"D"))
.append(new AnswerQuestion("一斤棉花和一斤铁那个重","一样重"))
.append(new AnswerQuestion("请用四个数字阵形容厂长","4396"));
//此处省略多题。。。。
public String create(String id, String name) throwsCloneNotSupportedException {
Question questionClone = (Question)question.clone();
questionClone.setCandidate(name);
questionClone.setNumber(id);
return questionClone.toString();
}
测试
@Test
public void test() throws CloneNotSupportedException {
QuestionHander questionHander= new QuestionHander();
System.out.println(QuestionHander .createPaper("clearlove","001"));
System.out.println(QuestionHander .createPaper("UZI","002"));
System.out.println(QuestionHander .createPaper("Looper","003"));
}
总结:原型模式实际中用到的情况相对较少,但是它的优点在于便于通过克隆⽅式创建复杂对象、也可以避免重复做初始化操作、不需要与类中所属的其他类耦合等。但也有⼀些缺点如果对象中包括了循环引⽤的克隆,以及类中深度使⽤对象的克隆,都会使此模式变得异常麻烦。总之设计模式只是一种思想,切莫为了设计模式而设计模式。