《一天一个设计模式》------原型模式(Prototype)

简介: 《一天一个设计模式》------原型模式(Prototype)

一:模式定义


**原型模式(Prototype Pattern)用于创建重复的对象,同时又能保证性能。**这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。


这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。也即:使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。


二:模式结构


原型模式包含如下角色:


Prototype :原型类

ConcretePrototype :具体的原型类


20210613212603588.png


三:应用-克隆羊问题


现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom 羊 属性完全相同的 10

只羊。

传统方式解决克隆羊问题:

package com.atguigu.prototype;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//传统的方法
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//....
System.out.println(sheep); System.out.println(sheep2); System.out.println(sheep3); System.out.println(sheep4); System.out.println(sheep5);
//...
}
}

传统方式优缺点:


优点是比较好理解,简单易操作。

在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低

总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活

改进的思路分析

思路:Java 中 Object 类是所有类的根类,Object 类提供了一个 clone()方法,该方法可以将一个 Java 对象复制一份,但是需要实现 clone 的 Java 类必须要实现一个接口 Cloneable,该接口表示该类能够复制且具有复制的能力 =>原型模式


原型模式创建克隆羊:


Sheep类:

public class Sheep implements Cloneable {
  private String name;
  private int age;
  private String color;
  private String address = "蒙古羊";
  public Sheep friend; // 是对象, 克隆是会如何处理, 默认是浅拷贝
  public Sheep(String name, int age, String color) {
    super();
    this.name = name;
    this.age = age;
    this.color = color;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  public String getColor() {
    return color;
  }
  public void setColor(String color) {
    this.color = color;
  }
  @Override
  public String toString() {
    return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
  }
//克隆该实例,使用默认的 clone 方法来完成
  @Override
  protected Object clone() {
    Sheep sheep = null;
    try {
      sheep = (Sheep) super.clone();
    } catch (Exception e) {
// TODO: handle exception System.out.println(e.getMessage());
    }
// TODO Auto-generated method stub return sheep;
  }
}

Client类:

public class Client {
  public static void main(String[] args) {
    System.out.println("原型模式完成对象的创建");
// TODO Auto-generated method stub
    Sheep sheep = new Sheep("tom", 1, "白色");
    sheep.friend = new Sheep("jack", 2, "黑色");
    Sheep sheep2 = (Sheep) sheep.clone(); // 克隆Sheep sheep3 = (Sheep)sheep.clone(); //克隆Sheep sheep4 =
                        // (Sheep)sheep.clone(); //克隆Sheep sheep5 = (Sheep)sheep.clone(); //克隆
    System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());
    System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());
    System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());
    System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());
  }
}

四:深拷贝和浅拷贝


浅拷贝:


对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。

对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值

克隆羊属性中有个羊对象时,当这个羊进行多次克隆时,里面的属性对象引用会指向同一个地址。

深拷贝:


复制对象的所有基本数据类型的成员变量值


为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝


深拷贝实现方式 1:重写 clone 方法来实现深拷贝


深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)

代码示例:

public class DeepCloneableTarget implements Serializable, Cloneable {
  /**
  *
  */
  private static final long serialVersionUID = 1L;
  private String cloneName;
  private String cloneClass;
//构造器
  public DeepCloneableTarget(String cloneName, String cloneClass) {
    this.cloneName = cloneName;
    this.cloneClass = cloneClass;
  }
//因为该类的属性,都是 String ,  因此我们这里使用默认的 clone 完成即可
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}
public class DeepProtoType implements Serializable, Cloneable {
  public String name; // String 属 性
  public DeepCloneableTarget deepCloneableTarget;// 引用类型
  public DeepProtoType() {
    super();
  }
//深拷贝 - 方式 1  使用 clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和 String 的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType)deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();
// TODO Auto-generated method stub return deepProtoType;
}
//深拷贝 - 方式 2 通过对象的序列化实现 (推荐) public Object deepClone() {
//创建流对象ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null;
  try
  {
//序列化
    bos = new ByteArrayOutputStream();
    oos = new ObjectOutputStream(bos);
    oos.writeObject(this); // 当前这个对象以对象流的方式输出
//反序列化
    bis = new ByteArrayInputStream(bos.toByteArray());
    ois = new ObjectInputStream(bis);
    DeepProtoType copyObj = (DeepProtoType) ois.readObject();
    return copyObj;
  }catch(
  Exception e)
  {
// TODO: handle exception e.printStackTrace();
    return null;
  }finally
  {
//关闭流 try {
    bos.close();
    oos.close();
    bis.close();
    ois.close();
  }catch(Exception e2)
  {
// TODO: handle exception System.out.println(e2.getMessage());
  }
}
}
}
//Client.java
package com.atguigu.prototype.deepclone;
public class Client {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub DeepProtoType p = new DeepProtoType(); p.name = "宋江";
p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");
//方式 1  完成深拷贝
//  DeepProtoType p2 = (DeepProtoType) p.clone();
//
//  System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
//  System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
//方式 2  完成深拷贝
DeepProtoType p2 = (DeepProtoType) p.deepClone();
System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode()); System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
}
}

原型模式的优缺点:


1) 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率

2) 不用重新初始化对象,而是动态地获得对象运行时的状态

3) 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码

4) 在实现深克隆的时候可能需要比较复杂的代码

5) 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则,这点请同学们注意.

相关文章
|
2月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑】设计模式——原型模式
对比原型模式和传统方式的实现思路、代码方案、优缺点,阐述原型模式的使用场景,以及深拷贝、浅拷贝等相关概念,并扩展原型模式在Spring源码中的应用。
【Java笔记+踩坑】设计模式——原型模式
|
2月前
|
设计模式 Java
Java设计模式-原型模式(3)
Java设计模式-原型模式(3)
Java设计模式-原型模式(3)
|
4月前
|
设计模式
iLogtail设计模式问题之iLogtail中的原型模式是什么
iLogtail设计模式问题之iLogtail中的原型模式是什么
iLogtail设计模式问题之iLogtail中的原型模式是什么
|
4月前
|
设计模式 JavaScript
js设计模式【详解】—— 原型模式
js设计模式【详解】—— 原型模式
50 6
|
5月前
|
设计模式 Java
Java设计模式之原型模式详解
Java设计模式之原型模式详解
|
5月前
|
设计模式
原型模式-大话设计模式
原型模式-大话设计模式
|
5月前
|
设计模式 Java Spring
设计模式——原型模式
设计模式——原型模式
|
5月前
|
设计模式 存储 架构师
设计模式-值类型与引用类型、深拷贝与浅拷贝、原型模式详解
 如果拷贝的时候共享被引用的对象就是浅拷贝,如果被引用的对象也拷贝一份出来就是深拷贝。(深拷贝就是说重新new一个对象,然后把之前的那个对象的属性值在重新赋值给这个用户)
160 0
|
6月前
|
设计模式 测试技术 Go
[设计模式 Go实现] 创建型~ 原型模式
[设计模式 Go实现] 创建型~ 原型模式
|
6月前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~创建型] 对象的克隆~原型模式
[设计模式Java实现附plantuml源码~创建型] 对象的克隆~原型模式