原型模式(Prototype Pattern):通过原型实例指定创建对象的种类,并且通过克隆这些原型来创建新的对象。原型模式是一种创建型模式
完成原型模式一般需要3个角色,抽象原型类、具体原型类、客户类
抽象原型类:声明克隆方法的接口、抽象类或具体实现类。是所有具体原型类的公共父类
具体原型类:实现抽象原型类,实现抽象原型类中的克隆方法,并在克隆方法中返回自己的一个克隆对象
客户类:通过原型对象克隆自己创建新的对象
实例代码如下
定义抽象原型类,声明克隆方法
package com.design.prototype;
/**
* 抽象原型类
*/
public interface Prototype {
//声明克隆方法
Prototype clone();
}
定义具体原型类,实现抽象原型类,实现克隆方法
package com.design.prototype;
/**
* 具体原型类
*/
public class ConcretePrototype implements Prototype{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Prototype clone() {
Prototype prototype = new ConcretePrototype();
((ConcretePrototype) prototype).setName(this.name);
return prototype;
}
@Override
public String toString() {
return "ConcretePrototype{" +
"name='" + name + '\'' +
'}' + super.toString();
}
}
定义客户类,调用测试
package com.design.prototype;
public class TestMain {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype();
System.out.println(prototype);
//克隆 prototype1 对象
Prototype prototype1 = prototype.clone();
System.out.println(prototype1);
}
}
上述示例代码可以作为原型模式的通用实现,与编程语言特性无关
下边来谈 java 语言实现原型模式
java 语言实现原型模式
使用 java 语言实现原型模式很简单。了解 java 语言的人都知道,在 java 语言中所有的类都继承自 java.lang.Object 类,而 Object 类提供了一个 clone()方法,可以将 java 对象进行克隆。因此,使用 java 语言实现原型模式可以不定义抽象原型类,直接使用 Object 类提供的 clone()方法实现对 java 对象的克隆
示例代码如下
定义书籍类
注意:要实现克隆的 java 类必须实现 Cloneable 接口,表示这个 java 类支持复制
这里的 clone() 方法直接使用了父类 Object 的 clone()方法
package com.design.prototype.javaprototype;
import java.util.Date;
public class Book implements Cloneable{
public Book(String bookName, String bookAuthor, Date date){
this.bookName = bookName;
this.bookAuthor = bookAuthor;
this.date = date;
}
private String bookName;
private String bookAuthor;
private Date date;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookAuthor() {
return bookAuthor;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); //浅克隆
}
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
", bookAuthor='" + bookAuthor + '\'' +
", date=" + date +
'}'+ super.toString();
}
}
测试调用
package com.design.prototype.javaprototype;
import java.util.Date;
public class TestMain {
public static void main(String[] args) throws CloneNotSupportedException {
Book book = new Book("红楼梦", "曹雪芹", new Date());
Book book1 = (Book) book.clone();
System.out.println(book);
System.out.println(book1);
System.out.println(book == book1);
System.out.println(book.getBookName() == book1.getBookName());
}
}
测试结果如下图
通过打印出的结果可以看出,使用原型模式成功克隆出来 book1 对象
但是,如果仔细观察会发现 book 对象和 book1 对象的 bookName 属性的内存地址值完全相等,通过 debug 进一步分析
发现 book 对象和 book1 对象的属性确实指向了相同的内存地址,这里我们引入两个概念,浅克隆和深克隆
浅克隆:若原型对象的成员变量是值类型,则复制一份给克隆对象;若原型对象的成员变量是引用类型,则将引用对象的内存地址复制一份给克隆对象,即原型对象和克隆对象的成员变量都指向相同的内存地址。简单的讲,浅克隆只复制对象本身及其中包含的值类型的成员变量,引用类型的成员对象并没有被复制
深克隆:无论原型对象的成员变量是值类型还是引用类型,都复制一份给克隆对象
了解了浅克隆和深克隆的概念后,就知道上边的代码其实实现的是浅克隆的原型模式
下边代码演示深克隆的原型模式
这里自定义克隆方法
package com.design.prototype.javaprototype;
import java.io.*;
import java.util.Date;
public class DeepBook implements Serializable {
public DeepBook(String bookName, String bookAuthor, Date date){
this.bookName = bookName;
this.bookAuthor = bookAuthor;
this.date = date;
}
private String bookName;
private String bookAuthor;
private Date date;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookAuthor() {
return bookAuthor;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
/**
* 自定义深克隆方法
* @return
*/
public DeepBook clone(){
DeepBook deepBook = null;
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
try {
deepBook = (DeepBook) objectInputStream.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return deepBook;
}
}
测试调用
package com.design.prototype.javaprototype;
import java.util.Date;
public class TestMain {
public static void main(String[] args) throws CloneNotSupportedException {
DeepBook deepBook = new DeepBook("三国演义","罗贯中", new Date());
DeepBook deepBook1 = deepBook.clone();
System.out.println(deepBook.getBookName() == deepBook1.getBookName());
}
}
控制台打印 false,说明实现了深克隆
原型模式总结
优点:创建新的较为复杂的对象时,使用原型模式可以简化创建过程;扩展性好;深克隆方式可以保存对象状态
缺点:因为每个类配备一个克隆方法,所以对已有的类进行改造时,违背了开闭原则;深克隆编写较为复杂的代码比较麻烦
适用场景:创建新的对象成本较大,通过原型模式复制得到可以减少资源占用;构造函数比较复杂;循环体中创建大量对象