在设计模式中按照不同的处理方式共包含三大类:创建型模式、结构型模式和行为模式。
创建型模式
- 工厂方法模式(Factory Method Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
- 单例模式(Singleton Pattern)
定义
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
接下来举一个出题试卷的例子
假如一套试卷里面都是选择题。现在要求分为多套试卷的形式。让每个学生得到的全部题目是一样。但是题目顺序和选项顺序不一样。 普通做法是通过创建N个不同的试卷对象来满足要求。这样做的话创建对象的代价就比较大。 采用原型模式时。可以通过初始化创建一个对象。需要获取其他不同的试卷时。则在clone()方法中进行题目的排序,以及选项的排序重组
原型模式代码
/**
* 选择题
* @author: yangqiang
* @date 2021-07-19 19:22
*/
@Data
public class ChoiceQuestion {
/**
* 题目
* */
private String name;
/**
* 选项 A B C D
* */
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;
}
}
/**
* 试卷
* @author yangqiang
* @date 2021-07-19 19:22
*/
@Data
public class TestPaper implements Cloneable{
/**姓名*/
private String name;
/**学号*/
private Integer number;
/**考题*/
private List<ChoiceQuestion> choiceQuestionList;
@Override
protected Object clone() throws CloneNotSupportedException {
TestPaper testPaper =(TestPaper) super.clone();
List<ChoiceQuestion> choiceQuestionList = testPaper.getChoiceQuestionList();
//现在开始打乱顺序
//题目乱序
Collections.shuffle(choiceQuestionList);
reorder(choiceQuestionList);
return testPaper;
}
/**
* 给试卷的选项重新排序
* */
private void reorder(List<ChoiceQuestion> choiceQuestionList) {
choiceQuestionList.forEach(item->{
String key = item.getKey();
Map<String, String> optionMap = item.getOption();
List<String> keyList = new ArrayList<>(optionMap.keySet());
Collections.shuffle(keyList);
String answer = optionMap.get(key);
int i = 0;
Collection<String> values = optionMap.values();
Map<String, String> newOptionMap = new HashMap<>();
for(String value:values){
if(value.equals(answer)){
item.setKey(keyList.get(i));
}
newOptionMap.put(keyList.get(i),value);
i ++ ;
}
item.setOption(newOptionMap);
});
}
/**
* 预设好题目
* */
public TestPaper() {
List<ChoiceQuestion> choiceQuestionArrayList = new ArrayList<>();
Map<String, String> map1 = new HashMap<String, String>();
// A、2.4 B、24 C、240 D、0.024
map1.put("A", "2.4");
map1.put("B", "24");
map1.put("C", "240");
map1.put("D", "0.024");
choiceQuestionArrayList.add(new ChoiceQuestion("1、两数相除商是2.4,如果被除数扩大100倍,除数除以0.01,商是( )。",map1,"A"));
Map<String, String> map2 = new HashMap<String, String>();
// A、(a+b)b B、a(a+b) C、(a+0)b D、(a+1)b
map2.put("A", "(a+b)b");
map2.put("B", "a(a+b)");
map2.put("C", "(a+0)b");
map2.put("D", "(a+1)b");
choiceQuestionArrayList.add(new ChoiceQuestion("2、用乘法分配律可以将ab+b改写成( )。",map2,"D"));
Map<String, String> map3 = new HashMap<String, String>();
// A、锐角三角形 B、直角三角形 C、钝角三角形 D、以上答案都不对
map3.put("A", "锐角三角形");
map3.put("B", "直角三角形");
map3.put("C", "钝角三角形");
map3.put("D", "以上答案都不对");
choiceQuestionArrayList.add(new ChoiceQuestion("3、一个等腰三角形,一个底角与顶角度数的比是2: 1,则这个等腰三角形也是( )。",map3,"A"));
Map<String, String> map4 = new HashMap<String, String>();
// A、5千米 B、10千米 C、13 千米 D、30千米
map4.put("A", "5千米");
map4.put("B", "10千米;");
map4.put("C", "13 千米");
map4.put("D", "30千米");
choiceQuestionArrayList.add(new ChoiceQuestion("4、小明步行3小时走了20千米的路程,骑自行车沿原路返回刚好用1小时。小明往返的平均速度是每小时( )。",map4,"B"));
this.choiceQuestionList = choiceQuestionArrayList;
}
/**
* 获取clone的试卷
* */
public TestPaper getTestPaper(String name,Integer number){
this.name = name ;
this.number = number;
try {
this.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return this;
}
}
/**
* @author yangqiang
* @date 2021-07-19 20:01
*/
public class PrototypeTest {
public static void main(String[] args) {
TestPaper testPaper = new TestPaper();
TestPaper paper1 = testPaper.getTestPaper("a", 1);
List<ChoiceQuestion> choiceQuestionList = paper1.getChoiceQuestionList();
System.out.println(choiceQuestionList);
}
}
结果
[ChoiceQuestion(name=4、小明步行3小时走了20千米的路程,骑自行车沿原路返回刚好用1小时。小明往返的平均速度是每小时( )。, option={A=10千米;, B=5千米, C=13 千米, D=30千米}, key=A), ChoiceQuestion(name=1、两数相除商是2.4,如果被除数扩大100倍,除数除以0.01,商是( )。, option={A=24, B=240, C=0.024, D=2.4}, key=D), ChoiceQuestion(name=3、一个等腰三角形,一个底角与顶角度数的比是2: 1,则这个等腰三角形也是( )。, option={A=钝角三角形, B=直角三角形, C=以上答案都不对, D=锐角三角形}, key=D), ChoiceQuestion(name=2、用乘法分配律可以将ab+b改写成( )。, option={A=(a+1)b, B=a(a+b), C=(a+b)b, D=(a+0)b}, key=A)]
这样的话可以得到多个不同的对象。但是在创建过程中只创建了一个TestPaper对象
- 以上的实际场景模拟了原型模式在开发中重构的作用,但是原型模式的使用频率确实不是很高。如果有一些特殊场景需要使用到,也可以按照此设计模式进行优化。
- 另外原型设计模式的优点包括;便于通过克隆方式创建复杂对象、也可以避免重复做初始化操作、不需要与类中所属的其他类耦合等。但也有一些缺点如果对象中包括了循环引用的克隆,以及类中深度使用对象的克隆,都会使此模式变得异常麻烦。
- 终究设计模式是一整套的思想,在不同的场景合理的运用可以提升整体的架构的质量。永远不要想着去硬凑设计模式,否则将会引起过渡设计,以及在承接业务反复变化的需求时造成浪费的开发和维护成本。
- 初期是代码的优化,中期是设计模式的使用,后期是把控全局服务的搭建。不断的加强自己对全局能力的把控,也加深自己对细节的处理。可上可下才是一个程序员最佳处理方式,选取做合适的才是最好的选择。 扩展
深克隆与浅克隆
浅克隆:只克隆对象不克隆对象的引用对象属性 。造成克隆的对象的引用对象属性改变时 。原来的对象也跟着改变
深克隆:调用当前对象的clone()方法时也调用引用对象的clone()方法 这样克隆对象的引用对象和原来的引用对象不是一个。
浅克隆举例
/**
* @author yangqiang
* @date 2021-07-20 15:06
*/
@Data
public class Teacher implements Cloneable{
private String name;
private Student student;
public Teacher(String name, Student student) {
this.name = name;
this.student = student;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
* @author yangqiang
* @date 2021-07-20 15:06
*/
@Data
@AllArgsConstructor
public class Student {
private String name;
}
/**
* @author yangqiang
* @date 2021-07-20 15:07
*/
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher("teacher1",new Student("student1"));
Teacher clone = (Teacher) teacher.clone();
clone.getStudent().setName("student2");
System.out.println(teacher);
System.out.println(clone);
}
}
结果
Teacher(name=teacher1, student=Student(name=2))
Teacher(name=teacher1, student=Student(name=2))
浅克隆举例
/**
* @author yangqiang
* @date 2021-07-20 15:06
*/
@Data
public class Teacher implements Cloneable{
private String name;
private Student student;
public Teacher(String name, Student student) {
this.name = name;
this.student = student;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Teacher teacher = (Teacher)super.clone();
teacher.setStudent((Student)teacher.getStudent().clone());
return teacher;
}
}
/**
* @author yangqiang
* @date 2021-07-20 15:06
*/
@Data
@AllArgsConstructor
public class Student implements Cloneable{
private String name;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
结果
Teacher(name=teacher1, student=Student(name=student1))
Teacher(name=teacher1, student=Student(name=student2))