https://www.runoob.com/design-pattern/prototype-pattern.html

1. 概念

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

2. 介绍

意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
主要解决:在运行期建立和删除原型。
何时使用:
1、当一个系统应该独立于它的产品创建,构成和表示时。
2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。
3、为了避免创建一个与产品类层次平行的工厂类层次时。
4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。
关键代码: 1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone(),在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些”易变类”拥有稳定的接口。
应用实例: 1、细胞分裂。 2、JAVA 中的 Object clone() 方法。
优点: 1、性能提高。 2、逃避构造函数的约束。
缺点:
1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
2、必须实现 Cloneable 接口。
使用场景:
1、资源优化场景。
2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3、性能和安全要求的场景。
4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5、一个对象多个修改者的场景。
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂 方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

3. 代码

我们将创建一个抽象类 Shape 和扩展了 Shape 类的实体类。下一步是定义类 ShapeCache,该类把 shape 对象存储在一个 Hashtable 中,并在请求的时候返回它们的克隆。
PrototypePatternDemo 类使用 ShapeCache 类来获取 Shape 对象。
image.png

总的shape类

  1. public abstract class Shape implements Cloneable{
  2. //形状id
  3. private String id;
  4. //形状类型
  5. protected String type;
  6. //画
  7. abstract void draw();
  8. public String getType(){
  9. return type;
  10. }
  11. public String getId() {
  12. return id;
  13. }
  14. public void setId(String id) {
  15. this.id = id;
  16. }
  17. @Override
  18. public Object clone() {
  19. Object clone = null;
  20. try {
  21. clone = super.clone();
  22. } catch (CloneNotSupportedException e) {
  23. e.printStackTrace();
  24. }
  25. return clone;
  26. }
  27. }

具体子类

  1. public class Circle extends Shape{
  2. public Circle() {
  3. type = "圆形";
  4. }
  5. @Override
  6. public void draw() {
  7. System.out.println("画一个圆形");
  8. }
  9. }
  10. public class Rectangle extends Shape{
  11. public Rectangle() {
  12. type = "长方形";
  13. }
  14. @Override
  15. public void draw() {
  16. System.out.println("画一个长方形");
  17. }
  18. }
  19. public class Square extends Shape{
  20. public Square() {
  21. type = "正方形";
  22. }
  23. @Override
  24. public void draw() {
  25. System.out.println("画一个正方形");
  26. }
  27. }

缓存类

  1. //缓存图形
  2. public class ShapeCache {
  3. //添加进
  4. private static Hashtable<String, Shape> shapeMap
  5. = new Hashtable<String, Shape>();
  6. //在缓存中找出类
  7. public static Shape getShape(String shapeId) {
  8. Shape cachedShape = shapeMap.get(shapeId);
  9. return (Shape) cachedShape.clone();
  10. }
  11. //这一步,我们可以理解成从数据库中把查询出来的类加到map中去
  12. public static void loadCache() {
  13. Circle circle = new Circle();
  14. circle.setId("1");
  15. shapeMap.put(circle.getId(),circle);
  16. Square square = new Square();
  17. square.setId("2");
  18. shapeMap.put(square.getId(),square);
  19. Rectangle rectangle = new Rectangle();
  20. rectangle.setId("3");
  21. shapeMap.put(rectangle.getId(),rectangle);
  22. }
  23. }

测试类

  1. public class PrototypePatternDemo {
  2. public static void main(String[] args) {
  3. //先从数据库加载到缓存中
  4. ShapeCache.loadCache();
  5. //值得注意的是:这里的形状是从缓存map中拿出来的
  6. Shape clonedShape = (Shape) ShapeCache.getShape("1");
  7. System.out.println("Shape : " + clonedShape.getType());
  8. Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
  9. System.out.println("Shape : " + clonedShape2.getType());
  10. Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
  11. System.out.println("Shape : " + clonedShape3.getType());
  12. //Shape : 圆形
  13. //Shape : 正方形
  14. //Shape : 长方形
  15. }
  16. }

4. 谈谈clone

clone:克隆,字面意思就是根据已有对象创建出一个新的一模一样的对象

https://blog.csdn.net/riemann_/article/details/87217229?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163022812916780274139081%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163022812916780274139081&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-87217229.first_rank_v2_pc_rank_v29&utm_term=%E6%B7%B1%E6%8B%B7%E8%B4%9D%E5%92%8C%E6%B5%85%E6%8B%B7%E8%B4%9D&spm=1018.2226.3001.4187

4.1 浅拷贝

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。
简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
image.png

  1. public class Teacher implements Cloneable{
  2. private String name;
  3. private int age;
  4. public String getName() {
  5. return name;
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public int getAge() {
  11. return age;
  12. }
  13. public void setAge(int age) {
  14. this.age = age;
  15. }
  16. }
  17. public class Student implements Cloneable{
  18. private Teacher teacher;
  19. private String name;
  20. @Override
  21. public String toString() {
  22. return "Student{" +
  23. "teacher=" + teacher +
  24. ", name='" + name + '\'' +
  25. '}';
  26. }
  27. public Teacher getTeacher() {
  28. return teacher;
  29. }
  30. public void setTeacher(Teacher teacher) {
  31. this.teacher = teacher;
  32. }
  33. public String getName() {
  34. return name;
  35. }
  36. public void setName(String name) {
  37. this.name = name;
  38. }
  39. public Student() {
  40. }
  41. @Override
  42. public Object clone() throws CloneNotSupportedException {
  43. Object object = super.clone();
  44. return object;
  45. }
  46. }
  1. public class ShallowClone {
  2. public static void main(String[] args) throws Exception {
  3. Teacher teacher = new Teacher();
  4. teacher.setName("修改前");
  5. teacher.setAge(18);
  6. Student student1 = new Student();
  7. student1.setName("李四");
  8. student1.setTeacher(teacher);
  9. Student student2 = (Student) student1.clone();
  10. System.out.println("=============克隆====================");
  11. System.out.println(student2.getName()); //李四
  12. System.out.println(student2.getTeacher().getName());//修改前
  13. System.out.println(student2.getTeacher().getAge());//18
  14. System.out.println("==============修改后==================");
  15. teacher.setName("修改后");
  16. System.out.println(student2.getTeacher().getName());//修改后
  17. System.out.println(student2.getTeacher().getAge());//18
  18. }
  19. }

可以看到修改了student1里面的teacher,student2里面的teacher也变了,证明克隆过后两个stu的teacher指向的是同一个。这就是浅拷贝

4.2 深拷贝

下面例子可以看到,修改了第一个stu的teacher信息,第二个stu的teacher信息不会变

  1. public class ShallowClone {
  2. public static void main(String[] args) throws Exception {
  3. Teacher teacher = new Teacher();
  4. teacher.setName("修改前");
  5. teacher.setAge(18);
  6. Student student1 = new Student();
  7. student1.setName("李四");
  8. student1.setTeacher(teacher);
  9. Student student2 = (Student) student1.clone();
  10. System.out.println("=============克隆====================");
  11. System.out.println(student2.getName()); //李四
  12. System.out.println(student2.getTeacher().getName());//修改前
  13. System.out.println(student2.getTeacher().getAge());//18
  14. System.out.println("==============修改后==================");
  15. teacher.setName("修改后");
  16. System.out.println(student1.getTeacher().getName());//修改后
  17. System.out.println(student2.getTeacher().getName());//修改前
  18. }
  19. }
  20. public class Student implements Cloneable{
  21. private Teacher teacher;
  22. private String name;
  23. @Override
  24. public String toString() {
  25. return "Student{" +
  26. "teacher=" + teacher +
  27. ", name='" + name + '\'' +
  28. '}';
  29. }
  30. public Teacher getTeacher() {
  31. return teacher;
  32. }
  33. public void setTeacher(Teacher teacher) {
  34. this.teacher = teacher;
  35. }
  36. public String getName() {
  37. return name;
  38. }
  39. public void setName(String name) {
  40. this.name = name;
  41. }
  42. public Student() {
  43. }
  44. @Override
  45. public Object clone() throws CloneNotSupportedException {
  46. // 改为深复制:
  47. Student student = (Student) super.clone();
  48. // 本来是浅复制,现在将Teacher对象复制一份并重新set进来,这时候两个teacher就是不同的了
  49. student.setTeacher((Teacher) student.getTeacher().clone());
  50. return student;
  51. }
  52. }
  1. public class ShallowClone {
  2. public static void main(String[] args) throws Exception {
  3. Teacher teacher = new Teacher();
  4. teacher.setName("修改前");
  5. teacher.setAge(18);
  6. Student student1 = new Student();
  7. student1.setName("李四");
  8. student1.setTeacher(teacher);
  9. Student student2 = (Student) student1.clone();
  10. System.out.println("=============克隆====================");
  11. System.out.println(student2.getName()); //李四
  12. System.out.println(student2.getTeacher().getName());//修改前
  13. System.out.println(student2.getTeacher().getAge());//18
  14. System.out.println("==============修改后==================");
  15. teacher.setName("修改后");
  16. System.out.println(student1.getTeacher().getName());//修改后
  17. System.out.println(student2.getTeacher().getName());//修改前
  18. }
  19. }

image.png