博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式之原型模式
阅读量:6994 次
发布时间:2019-06-27

本文共 10462 字,大约阅读时间需要 34 分钟。

1.定义

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

面向对象语言中,拷贝分为两类: 
1. 浅拷贝 
对于基础数据类型(如Java中short/int/long/float/double/char/byte/boolean),直接复制数值,对于引用数据类型,则直接复制引用,修改此引用指向的对象会对原引用对象造成影响 
2. 深拷贝 
对于基础数据类型,直接复制数值,对于引用数据类型,则拷贝一份引用对象至分配到的新内存中,修改此对象对原引用对象不会有影响

2.UML图

 

Client:客户端用户,提出创建克隆对象的请求 

Prototype:原型接口,标识对象是否可以克隆 
ConcretePrototye:具体实例,实现了原型接口的类,通过clone()方法可返回自身的一个副本

3.实现

Java中的类都继承自 java.lang.Object。Object 类提供有一个 clone() 方法,可以将一个 Java 对象复制一份。因此在 Java 中可以直接使用 Object 提供的 clone() 方法来实现对象的克隆。 

需要注意的是能够实现克隆的 Java 类都必须实现一个标识接口 Cloneable,表示这个 Java 类支持被复制。如果一个类没有实现这个接口但是调用了 clone() 方法,Java 编译器将抛出一个 CloneNotSupportedException 异常。 
以下以持有手机的个人的克隆为示例 
1. 简单形式 
个人所持有的手机Phone类

1 package com.creasy.testjava; 2  3 public class Phone{ 4  5     private String phone_brand; 6     private String phone_number; 7  8     public Phone() { 9     }10 11     public void setBrandAndNumber(String phone_brand, String phone_number) {12         this.phone_brand = phone_brand;13         this.phone_number = phone_number;14     }15 16     public void Display() {17         System.out.print("phone_brand:" + phone_brand + " and phone_number:" + phone_number);18     }19 20 }

个人类Person

1 package com.creasy.testjava; 2  3 public class Person implements Cloneable { 4  5     private String name; 6     private int age; 7     private Phone phone; 8  9     public Person() {10     }11 12     @Override13     protected Object clone() throws CloneNotSupportedException {14         Person p = (Person)super.clone();15         return p;16     }17 18     public void setNameAndAge(String name, int age, Phone phone) {19         this.name = name;20         this.age = age;21         this.phone = phone;22     }23 24     public void Display() {25         System.out.print("name:" + name + " and age:" + age + " ");26         if (null == phone) {27             System.out.println("has no phone");28         } else {29             phone.Display();30         }31         System.out.println();32     }33 34     public String getName() {35         return name;36     }37 38     public int getAge() {39         return age;40     }41 42     public Phone getPhone() {43         return phone;44     }45 46 }

客户端调用:

1 package com.creasy.testjava; 2  3 /** 4  * @author laicreasy 5  * 6  */ 7 public class TestBaseJavaFunction { 8  9     public static void main(String[] args) {10         try {11             Person a = new Person();12             Phone phone = new Phone();13             phone.setBrandAndNumber("iPhone", "1234567890");14             a.setNameAndAge("xiaoli", 18, phone);15             Person b = (Person) a.clone();16             a.Display();17             phone.setBrandAndNumber("Android", "0987654321");18             a.Display();19             b.Display();20             System.out.println(a.getPhone() == b.getPhone());21         } catch (CloneNotSupportedException e) {22             e.printStackTrace();23         }24     }25 26 }

输出结果:

1 name:xiaoli and age:18 phone_brand:iPhone and phone_number:12345678902 name:xiaoli and age:18 phone_brand:Android and phone_number:09876543213 name:xiaoli and age:18 phone_brand:Android and phone_number:09876543214 true

由结果可以看出,Person中Phone的克隆只是简单的复制了引用给到新对象。如果Phone类改成

1 package com.creasy.testjava; 2  3 public class Phone implements Cloneable{ 4  5     private String phone_brand; 6     private String phone_number; 7  8     public Phone() { 9     }10 11     public void setBrandAndNumber(String phone_brand, String phone_number) {12         this.phone_brand = phone_brand;13         this.phone_number = phone_number;14     }15 16     public void Display() {17         System.out.print("phone_brand:" + phone_brand + " and phone_number:" + phone_number);18     }19 20     @Override21     protected Object clone() throws CloneNotSupportedException {22         return super.clone();23     }24 }

而Person类中的clone方法改成

1 而Person类中的clone方法改成2 3 @Override4     protected Object clone() throws CloneNotSupportedException {5         Person p = (Person)super.clone();6         p.phone = (Phone)this.phone.clone();7         return p;8     }

再次运行,得到结果

1 name:xiaoli and age:18 phone_brand:iPhone and phone_number:12345678902 name:xiaoli and age:18 phone_brand:Android and phone_number:09876543213 name:xiaoli and age:18 phone_brand:iPhone and phone_number:12345678904 false

由结果可以看到,此时则是深层复制,a和b中的phone指向的是不同的对象的 

2. 实现Serializable形式 
序列化的过程就是把数据从内存中写入流中,反序列化则是从流中把数据再写入新内存中,通过序列化和反序列化则可以实现对象的深层复制 
Phone类

1 package com.creasy.testjava; 2  3 import java.io.Serializable; 4  5 public class Phone implements Serializable{ 6  7     private static final long serialVersionUID = 8785807031027660303L; 8  9     private String phone_brand;10     private String phone_number;11 12     public Phone() {13     }14 15     public void setBrandAndNumber(String phone_brand, String phone_number) {16         this.phone_brand = phone_brand;17         this.phone_number = phone_number;18     }19 20     public void Display() {21         System.out.print("phone_brand:" + phone_brand + " and phone_number:" + phone_number);22     }23 24 }

Person类:

1 package com.creasy.testjava; 2  3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.IOException; 6 import java.io.ObjectInputStream; 7 import java.io.ObjectOutputStream; 8 import java.io.Serializable; 9 10 public class Person implements Serializable {11 12     /**13      * 14      */15     private static final long serialVersionUID = 5767629194216311414L;16 17     private String name;18     private int age;19     private Phone phone;20 21     public Person() {22     }23 24     public Person deepClone() throws IOException, ClassNotFoundException  {25         //将对象写入流中26         ByteArrayOutputStream bao=new  ByteArrayOutputStream();27         ObjectOutputStream oos=new  ObjectOutputStream(bao);28         oos.writeObject(this);29         //将对象从流中取出30         ByteArrayInputStream bis=new  ByteArrayInputStream(bao.toByteArray());31         ObjectInputStream ois=new  ObjectInputStream(bis);32         return  (Person)ois.readObject();33     }34 35     public void setNameAndAge(String name, int age, Phone phone) {36         this.name = name;37         this.age = age;38         this.phone = phone;39     }40 41     public void Display() {42         System.out.print("name:" + name + " and age:" + age + " ");43         if (null == phone) {44             System.out.println("has no phone");45         } else {46             phone.Display();47         }48         System.out.println();49     }50 51     public String getName() {52         return name;53     }54 55     public int getAge() {56         return age;57     }58 59     public Phone getPhone() {60         return phone;61     }62 63 }

客户端调用

1 package com.creasy.testjava; 2  3 /** 4  * @author laicreasy 5  * 6  */ 7 public class TestBaseJavaFunction { 8  9     public static void main(String[] args) {10         try {11             Person a = new Person();12             Phone phone = new Phone();13             phone.setBrandAndNumber("iPhone", "1234567890");14             a.setNameAndAge("xiaoli", 18, phone);15             Person b = (Person) a.deepClone();16             a.Display();17             phone.setBrandAndNumber("Android", "0987654321");18             a.Display();19             b.Display();20             System.out.println(a.getPhone() == b.getPhone());21         } catch (Exception e) {22             e.printStackTrace();23         }24     }25 26 }

运行结果

1 name:xiaoli and age:18 phone_brand:iPhone and phone_number:12345678902 name:xiaoli and age:18 phone_brand:Android and phone_number:09876543213 name:xiaoli and age:18 phone_brand:iPhone and phone_number:12345678904 false

由结果可以看出,对应a和对象b是不同的对象,并且他们各自持有不同的phone对象 

通过序列化及反序列化进行深层复制的前提是对象以及对象内部所有引用到的对象都是可序列化的,否则,需要声明不可序列化的对象成transient,从而将之排除在复制过程之外。关于transient关键字的描述,可参考:

4.Android或者Java中的使用

Android中,存储Integer和Object的键值对类SparseArray实现了Cloneable,直接看其clone源码,如下:

1 private int[] mKeys; 2     private Object[] mValues; 3  4     @Override 5     @SuppressWarnings("unchecked") 6     public SparseArray
clone() { 7 SparseArray
clone = null; 8 try { 9 clone = (SparseArray
) super.clone();10 clone.mKeys = mKeys.clone();11 clone.mValues = mValues.clone();12 } catch (CloneNotSupportedException cnse) {13 /* ignore */14 }15 return clone;16 }

使用SparseArray测试,代码及结果如下

1 public void testSparseArray() { 2         SparseArray
mSparseArray = new SparseArray
(); 3 Phone phone1 = new Phone(); 4 phone1.setBrandAndNumber("SANXING", "123456789"); 5 Phone phone2 = new Phone(); 6 phone2.setBrandAndNumber("APPLE", "123456789"); 7 mSparseArray.put(0, phone1); 8 mSparseArray.put(1, phone2); 9 //mSparseArray110 for( int i=0; i < mSparseArray.size(); i++ ) {11 Phone p = mSparseArray.get(i);12 System.out.println("phone_brand:" + p.getPhone_brand() + " and phone_number:" + p.getPhone_number());13 }14 SparseArray
mSparseArray2 = mSparseArray.clone();15 for( int i=0; i < mSparseArray2.size(); i++ ) {16 Phone p = mSparseArray.get(i);17 p.setBrandAndNumber(p.getPhone_brand() + "2", p.getPhone_number() + "2");18 }19 //mSparseArray220 for( int i=0; i < mSparseArray2.size(); i++ ) {21 Phone p = mSparseArray2.get(i);22 System.out.println("phone_brand:" + p.getPhone_brand() + " and phone_number:" + p.getPhone_number());23 }24 //mSparseArray125 for( int i=0; i < mSparseArray.size(); i++ ) {26 Phone p = mSparseArray.get(i);27 System.out.println("phone_brand:" + p.getPhone_brand() + " and phone_number:" + p.getPhone_number());28 }29 //mSparseArray230 for( int i=0; i < mSparseArray2.size(); i++ ) {31 Phone p = mSparseArray2.get(i);32 System.out.println("phone_brand:" + p.getPhone_brand() + " and phone_number:" + p.getPhone_number());33 }34 System.out.println(mSparseArray == mSparseArray2);35 }

结果 

由上,可见SparseArray中clone方法执行的是浅层复制功能(实际上SparseArray中存储的Object为数组,而数组的clone执行的是浅层复制功能)

5.使用场景

(1) 创建新对象成本较大(如初始化需要占用较长的时间,占用太多的 CPU 资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得 

(2)当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候,不适合使用原型模式

参考博客: 

 

 

转载于:https://www.cnblogs.com/creasylai19/p/4882059.html

你可能感兴趣的文章
swift-UserDefaults控制账号和密码
查看>>
(一)html之基本结构
查看>>
bootstrop
查看>>
文字过长隐藏
查看>>
02 用户注册通过发送邮箱激活
查看>>
通过XDocument方式把List写入Xml文件
查看>>
zTree根节点默认打开
查看>>
【BZOJ5306】 [Haoi2018]染色
查看>>
linux操作系统不重启添加raid0步骤
查看>>
C#判断程序是由Windows服务启动还是用户启动
查看>>
【BZOJ1123】 [POI2008]BLO (tarjan)
查看>>
Django中的CSRF
查看>>
跨年错月查询数据
查看>>
PHP安装时踩坑
查看>>
确定对象被使用前已经被初始化
查看>>
Java中的Filter过滤器
查看>>
jquery实现下拉框多选
查看>>
套接字超时
查看>>
sql server存储过程
查看>>
常见的HTTP返回码如4xx, 5xx
查看>>