您现在的位置是:亿华云 > 人工智能

设计模式系列—原型模式

亿华云2025-10-02 18:58:36【人工智能】7人已围观

简介前言23种设计模式速记 单例singleton)模式 工厂方法factory method)模式 抽象工厂abstract factory)模式

 前言

23种设计模式速记 单例(singleton)模式 工厂方法(factory method)模式 抽象工厂(abstract factory)模式 建造者/构建器(builder)模式

23种设计模式快速记忆的设计请看上面第一篇,本篇和大家一起来学习原型模式,模式模式在学习原型模式之前我们需要先认识下浅拷贝和深拷贝这两个概念。系列

浅拷贝和深拷贝浅拷贝浅拷贝是原型按位拷贝对象,它会创建一个新对象,设计这个对象有着原始对象属性值的模式模式一份精确拷贝。如果属性是系列基本类型,拷贝的原型就是基本类型的值;如果属性是内存地址(引用类型),拷贝的设计就是内存地址 ,因此如果其中一个对象改变了这个地址,模式模式就会影响到另一个对象。系列

复制了对象的原型引用地址,两个对象指向同一个内存地址,设计所以修改其中任意的模式模式值,另一个值都会随之变化。系列

深拷贝深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。亿华云计算

将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变。

模式定义用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式其实就是一个对象在创建另一个可定制的对象,而且不需要指定任何创建的细节。Java提供了Coneable接口,其中有一个唯一方法Clone(),实现这个接口就可以完成原型模式了。

实例说明浅拷贝案例声明 User 实体类,需要实现 Cloneable 接口,并覆写 clone() 方法。

User 属性包括基础数据类型和引用数据类型,方便演示

package com.niuh.designpattern.prototype; /**  * 用户信息  */ public class User implements Cloneable {      // 基础数据类型     private int id;     private String name;     private String sex;     private String pwd;     // 引用数据类型     private BaseInfo baseInfo;     public User(int id, String name, String sex, String pwd, BaseInfo baseInfo) {          this.id = id;         this.name = name;         this.sex = sex;         this.pwd = pwd;         this.baseInfo = baseInfo;     }     public int getId() {          return id;     }     public void setId(int id) {          this.id = id;     }     public String getName() {          return name;     }     public void setName(String name) {          this.name = name;     }     public String getSex() {          return sex;     }     public void setSex(String sex) {          this.sex = sex;     }     public String getPwd() {          return pwd;     }     public void setPwd(String pwd) {          this.pwd = pwd;     }     public BaseInfo getBaseInfo() {          return baseInfo;     }     public void setBaseInfo(BaseInfo baseInfo) {          this.baseInfo = baseInfo;     }     @Override     public String toString() {          return "hashCode: " + super.hashCode() + ", User{ " +                 "id=" + id +                 ", name=" + name + \ +                 ", sex=" + sex + \ +                 ", pwd=" + pwd + \ +                 ", baseInfo=" + baseInfo +                 };     }     @Override     protected User clone() throws CloneNotSupportedException {          return (User) super.clone();     } } 

package com.niuh.designpattern.prototype; import java.util.Date; /**  * 基础类  */ public class BaseInfo {      private String desc;     // .......     public BaseInfo(String desc) {          this.desc = desc;     }     public String getDesc() {          return desc;     }     public void setDesc(String desc) {          this.desc = desc;     }     @Override     public String toString() {          return "BaseInfo{ " +                 "desc=" + desc +                 };     } } 

package com.niuh.designpattern.prototype; /**  * 原型设计模式  */ public class PrototypePattern {      public static void main(String[] args) throws CloneNotSupportedException {          BaseInfo baseInfo = new BaseInfo("张三");         User user1 = new User(1, "张三", "男", "123456", baseInfo);         // new User  ......         // 克隆机制         User user2 = user1.clone();         user2.setId(2);         user2.setName("李四");         BaseInfo baseInfo1 = user2.getBaseInfo();         baseInfo1.setDesc("李四");         System.out.println(user1);         System.out.println(user2);     } } 

输出结果如下:

由输出的结果可见,通过 user1.clone() 拷贝对象后得到的 user2,和 user1 是两个不同的对象,HashCode 值不一样。user1 和 user2 的基础数据类型的修改互不影响,而引用类型 baseInfo 修改后是会有影响的。云服务器提供商

深拷贝案例通过上面的例子可以看到,浅拷贝会带来数据安全方面的隐患,例如我们只是想修改了 user2 的 baseInfo,但是 user1 的 baseInfo 也被修改了,因为它们都是指向的同一个地址。所以,此种情况下,我们需要用到深拷贝。

深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。

对于 User 的引用类型的成员变量 BaseInfo ,需要实现 Cloneable 并重写 clone() 方法。

package com.niuh.designpattern.prototype; import java.util.Date; /**  * 基础类  */ public class BaseInfo implements Cloneable {      private String desc;     // .......     public BaseInfo(String desc) {          this.desc = desc;     }     public String getDesc() {          return desc;     }     public void setDesc(String desc) {          this.desc = desc;     }     @Override     public String toString() {          return "BaseInfo{ " +                 "desc=" + desc +                 };     }     @Override     protected BaseInfo clone() throws CloneNotSupportedException {          //BaseInfo 如果也有引用类型的成员属性,也应该和 User 类一样实现         return (BaseInfo) super.clone();     } } 

在 User 的 clone() 方法中,需要拿到拷贝自己后产生的新的亿华云对象,然后对新的对象的引用类型再调用拷贝操作,实现对引用类型成员变量的深拷贝。

package com.niuh.designpattern.prototype; /**  * 用户信息  */ public class User implements Cloneable {      // 基础数据类型     private int id;     private String name;     private String sex;     private String pwd;     // 引用数据类型     private BaseInfo baseInfo;     public User(int id, String name, String sex, String pwd, BaseInfo baseInfo) {          this.id = id;         this.name = name;         this.sex = sex;         this.pwd = pwd;         this.baseInfo = baseInfo;     }     public int getId() {          return id;     }     public void setId(int id) {          this.id = id;     }     public String getName() {          return name;     }     public void setName(String name) {          this.name = name;     }     public String getSex() {          return sex;     }     public void setSex(String sex) {          this.sex = sex;     }     public String getPwd() {          return pwd;     }     public void setPwd(String pwd) {          this.pwd = pwd;     }     public BaseInfo getBaseInfo() {          return baseInfo;     }     public void setBaseInfo(BaseInfo baseInfo) {          this.baseInfo = baseInfo;     }     @Override     public String toString() {          return "hashCode: " + super.hashCode() + ", User{ " +                 "id=" + id +                 ", name=" + name + \ +                 ", sex=" + sex + \ +                 ", pwd=" + pwd + \ +                 ", baseInfo=" + baseInfo +                 };     }     @Override     protected User clone() throws CloneNotSupportedException {          // 深拷贝         User user = (User) super.clone();         user.baseInfo = baseInfo.clone();         return user;     } } 

与上面的使用方式一样,输出结果如下:

由输出结果可见,深拷贝后,不管是基础数据类型还是引用类型的成员变量,修改其值都不会相互造成影响。

序列化机制实现深拷贝需要在 User 类实现 Serializable,成员类型(BaseInfo)也需要实现 Serializable 接口。

package com.niuh.designpattern.prototype; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /**  * 用户信息  */ public class User implements Cloneable , Serializable {      // 基础数据类型     private int id;     private String name;     private String sex;     private String pwd;     // 引用数据类型     private BaseInfo baseInfo;     public User(int id, String name, String sex, String pwd, BaseInfo baseInfo) {          this.id = id;         this.name = name;         this.sex = sex;         this.pwd = pwd;         this.baseInfo = baseInfo;     }     public int getId() {          return id;     }     public void setId(int id) {          this.id = id;     }     public String getName() {          return name;     }     public void setName(String name) {          this.name = name;     }     public String getSex() {          return sex;     }     public void setSex(String sex) {          this.sex = sex;     }     public String getPwd() {          return pwd;     }     public void setPwd(String pwd) {          this.pwd = pwd;     }     public BaseInfo getBaseInfo() {          return baseInfo;     }     public void setBaseInfo(BaseInfo baseInfo) {          this.baseInfo = baseInfo;     }     @Override     public String toString() {          return "hashCode: " + super.hashCode() + ", User{ " +                 "id=" + id +                 ", name=" + name + \ +                 ", sex=" + sex + \ +                 ", pwd=" + pwd + \ +                 ", baseInfo=" + baseInfo +                 };     }     @Override     protected User clone() throws CloneNotSupportedException {          // 深拷贝         // User user = (User) super.clone();         // user.baseInfo = baseInfo.clone();         // return user;         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();         try (ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream)) {              oos.writeObject(this);         } catch (IOException e) {              e.printStackTrace();         }         ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());         try (ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream)) {              try {                  User user = (User) ois.readObject();                 return user;             } catch (ClassNotFoundException e) {                  e.printStackTrace();             }         } catch (IOException e) {              e.printStackTrace();         }         return null;     } } 

这个时候并没有使用Java深拷贝,改变成员属性Baseinfo,也能保存对象的独立性。

通过序列化机制来完成深拷贝不推荐使用,因为序列化操作是CPU密集型,解析流是比较消耗性能,速度会比较慢

优点

可以不耦合具体类的情况下克隆对象 避免重复的初始化代码 更方便的构建复杂对象

缺点

适用性不是很广。 每一个类必须配备一个克隆方法。 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。

应用场景当代码不应该依赖于需要复制的对象的具体类时,请使用Prototype模式。

某些结构复杂的对象的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口; 一般在初始化的信息不发生变化的情况下,克隆是最好的方法。

源码中的应用

#Spring  org.springframework.beans.factory.support.AbstractBeanDefinition #JDK java.util.Arrays java.util.ArrayList ...... 

ArrayList中的使用ArrayList也有clone()方法,如下

返回一个Object对象,所以在使用此方法的时候要强制转换。 ArrayList的本质是维护了一个Object的数组,所以克隆也是通过数组的复制实现的,属于浅复制。 @Override    public Object clone() {      try {          ArrayList<?> result = (ArrayList<?>) super.clone();         result.array = array.clone();         return result;     } catch (CloneNotSupportedException e) {         throw new AssertionError();     } } 

ArrayList的Clone浅复制的巧妙使用当你需要使用remove方法移除掉集合中的对象,而非要修改集合中的对象的时候,可以选择使用。

//添加两个元素 Student stJack=new Student("Jack", 13); Student stTom=new Student("Tom", 15); list.add(stJack); list.add(stTom); //克隆 ArrayList listCopy=(ArrayList) list.clone(); //移除且不修改 listCopy.remove(1); System.out.println(list); System.out.println(listCopy); 

移除且不修改集合中的元素,只是在List内部的数组中移除了指向元素的地址,可以放心的使用clone。

PS:以上代码提交在 Github :https://github.com/Niuh-Study/niuh-designpatterns.git

很赞哦!(31862)