目录
  1. 1. 原型模式
    1. 1.1. 定义
    2. 1.2. 原型模式示意图
    3. 1.3. 流程
    4. 1.4. 需求
    5. 1.5. 代码实现
    6. 1.6. 原型模式扩展
      1. 1.6.1. 需求
      2. 1.6.2. 代码实现
    7. 1.7. 应用场景
    8. 1.8. 原型模式的优点和注意事项
      1. 1.8.1. 优点
      2. 1.8.2. 注意事项
原型模式

原型模式

定义

Specify the kinds of objects to create using a prototypical instance , and create new objects by copying this prototye.
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建对象。

原型模式示意图

原型模式

流程

  1. 首先创建一个实体类,这个实体类需要实现Cloneable接口的clone()方法,这样能拥有克隆能力。
  2. 然后创建一个对象,根据客户端需要,以创建的这个对象作为原型,克隆多个需要的对象,然后通过set方法将这些克隆的对象改为自己需要的对象。

需求

我们为幼儿园小朋友发成绩表,他们的年龄和成绩都一样,我们只需要改成绩表的名字就行了。

代码实现

  1. 创建一个学生类,作为拷贝原型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* 原型模式需要实现Cloneable接口
*/
@Data
@ToString
public class Student implements Cloneable {
private String name ;
private String age ;
private String score ;
private ArrayList<Student> students = new ArrayList<>();
private final ArrayList<Student> finalStudent = new ArrayList<>();

public Student(String name, String age, String score) {
this.name = name;
this.age = age;
this.score = score;
System.out.println(name + "创建成功");
}

/**
* 这个方法是原型模式的基础
* @return
* @throws CloneNotSupportedException
*/
public Student clone() {
System.out.println("学生拷贝成功");
Student student = null ;
try {
// 这是一种浅拷贝,只能拷贝基本类型(包括string),其他的数组这些引用类型就与原型对象共享。
student = (Student) super.clone() ;
// 这个是深拷贝,将数组和引用一起拷贝,每个拷贝的对象有自己的数组和引用
this.students = (ArrayList<Student>) this.students.clone();

// 这个finalStudent是由final修饰,,不能重新赋值,不能拷贝
// this.finalStudent = (ArrayList<Student>) this.finalStudent.clone() ;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}

return student ;
}
}
  1. 开始发成绩表了
1
2
3
4
5
6
7
8
9
10
public class Test {
public static void main(String[] args) {
Student rack = new Student("rack", "12", "16");
Student clone = (Student) rack.clone();
clone.setName("leen");

System.out.println(rack);
System.out.println(clone);
}
}

原型模式扩展

为原型模式增加一个原型管理器,用来管理原型的创建。

需求

现在我们要创建一个学校,学校里面有教师,学生等实体,我们需要以一个教师和学生作为原型,创建出多个教师和学生。

代码实现

  1. 创建一个学校接口,里面放入clone方法,使得所有实现它的实体都需要重写clone,拥有复制的能力。
1
2
3
public interface School extends Cloneable {
public School clone();
}
  1. 创建一个学生类,实现学校接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Data
@ToString
public class Student implements School {
private String name ;
private String age ;
private String score ;
private ArrayList<Student> students = new ArrayList<>();
private final ArrayList<Student> finalStudent = new ArrayList<>();

public Student(String name, String age, String score) {
this.name = name;
this.age = age;
this.score = score;
System.out.println(name + "创建成功");
}

/**
* 这个方法是原型模式的基础
* @return
* @throws CloneNotSupportedException
*/
public Student clone() {
System.out.println("学生拷贝成功");
Student student = null ;
try {
// 这是一种浅拷贝,只能拷贝基本类型(包括string),其他的数组这些引用类型就与原型对象共享。
student = (Student) super.clone() ;
// 这个是深拷贝,将数组和引用一起拷贝,每个拷贝的对象有自己的数组和引用
this.students = (ArrayList<Student>) this.students.clone();

// 这个finalStudent是由final修饰,,不能重新赋值,不能拷贝
// this.finalStudent = (ArrayList<Student>) this.finalStudent.clone() ;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}

return student ;
}
}
  1. 创建一个教师类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Data
@ToString
public class Teacher implements School {
private String name ;
private String age ;
private String subject ;

public Teacher(String name, String age, String subject) {
this.name = name;
this.age = age;
this.subject = subject;
}

public Teacher clone(){
Teacher teacher = null ;
try {
teacher = (Teacher) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}

return teacher ;
}
}
  1. 创建一个原型管理器,我们如果需要更多的职位,比如校长等,我们直接通过原型管理器将原型对象加入进去,使其能拥有复制能力。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class PrototypeManager {
public Map<String , School> map = new HashMap<>();

/**
* 增加除了教师学生之外的学校职位
* @param key
* @param value
*/
public void add(String key , School value){
this.map.put(key , value);
}

/**
* 从原型池中获取原型,如果原型不存在,抛异常
* @param key
* @return
*/
public School get(String key){
School school = null;
try {
school = this.map.get(key);
}catch (Exception e){
e.printStackTrace();
}
return school ;
}
}
  1. 场景实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test {
public static void main(String[] args) {
PrototypeManager prototypeManager = new PrototypeManager();
prototypeManager.add("teacher" , new Teacher("teacher1" , "21" , "数学"));
Teacher teacher1 = (Teacher)prototypeManager.get("teacher");
Teacher teacher2 = teacher1.clone();
teacher2.setName("teacher2");

prototypeManager.add("student" , new Student("student1" , "12" , "45"));
Student student1 = (Student)prototypeManager.get("student");
Student student2 = student1.clone();
student2.setName("student2");

System.out.println(teacher1);
System.out.println(teacher2);
System.out.println(student1);
System.out.println(student2);
}
}

应用场景

  1. 一个对象有多个修改者。
  2. 类初始化需要很多资源,我们要优化的话,就直接克隆一个对象,不需要再创建。
  3. 通过new产生一个对象很繁琐,为了简化,我们直接copy就行了。
    原型模式就像我们复制粘贴一样,文章大体不变,只变一些细节。
    原型模式一般与工厂模式一起用;原型模式已经和java融为一体,随手就能用。

原型模式的优点和注意事项

优点

  1. 原型模式是在内存二进制流的拷贝,比直接new一个对象消耗性能少。
  2. 在内存中拷贝,构造函数不会被执行,这就逃避构造函数的约束。

注意事项

  1. 如果实体类中只有基本数据类型和string,就用浅拷贝进行克隆。
  2. 如果实体类中还有数组和引用,就需要单独为它们进行深拷贝。
  3. 使用clone后,克隆一个对象是直接拷贝二进制流,不会执行构造函数。如果需要使用构造函数,就不能使用原型模式。
  4. 变量前加了final关键字,就不能使用clone进行拷贝。
文章作者: rack-leen
文章链接: http://yoursite.com/2019/05/19/Java/Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F/%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 rack-leen's blog
打赏
  • 微信
  • 支付宝

评论