原型模式 (Prototype Pattern)
概述
原型模式是一种创建型设计模式,它通过复制现有实例来创建新的实例,而不是通过new关键字创建。原型模式允许一个对象再创建另外一个可定制的对象,而无需知道如何创建的细节。
意图
- 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象
适用场景
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载
- 为了避免创建一个与产品类层次平行的工厂类层次时
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些
结构
┌─────────────┐
│ Prototype │
├─────────────┤
│ + clone() │
└─────────────┘
▲
│
┌─────────────┐
│ConcretePrototype1│
├─────────────┤
│ + clone() │
└─────────────┘
┌─────────────┐
│ConcretePrototype2│
├─────────────┤
│ + clone() │
└─────────────┘
┌─────────────┐
│ Client │
├─────────────┤
│ operation() │
└─────────────┘
参与者
- Prototype:声明一个克隆自身的接口
- ConcretePrototype:实现一个克隆自身的操作
- Client:让一个原型克隆自身从而创建一个新的对象
示例代码
下面是一个完整的原型模式示例,以创建不同类型的图形为例:
java
// Prototype - 原型接口
public interface Shape extends Cloneable {
void draw();
Shape clone();
}
// ConcretePrototype1 - 具体原型1
public class Circle implements Shape {
private String color;
private int radius;
public Circle(String color, int radius) {
this.color = color;
this.radius = radius;
}
@Override
public void draw() {
System.out.println("绘制" + color + "的圆形,半径为" + radius);
}
@Override
public Shape clone() {
try {
return (Shape) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
// Getter和Setter方法
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
}
// ConcretePrototype2 - 具体原型2
public class Rectangle implements Shape {
private String color;
private int width;
private int height;
public Rectangle(String color, int width, int height) {
this.color = color;
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("绘制" + color + "的矩形,宽度为" + width + ",高度为" + height);
}
@Override
public Shape clone() {
try {
return (Shape) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
// Getter和Setter方法
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
// 原型管理器
public class ShapeCache {
private static Map<String, Shape> shapeMap = new HashMap<>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (cachedShape != null) ? cachedShape.clone() : null;
}
public static void loadCache() {
Circle circle = new Circle("红色", 10);
shapeMap.put("红色圆形", circle);
Rectangle rectangle = new Rectangle("蓝色", 20, 30);
shapeMap.put("蓝色矩形", rectangle);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 初始化缓存
ShapeCache.loadCache();
// 从缓存中获取克隆对象
Shape clonedShape1 = ShapeCache.getShape("红色圆形");
System.out.println("形状: " + clonedShape1.getClass().getSimpleName());
clonedShape1.draw();
Shape clonedShape2 = ShapeCache.getShape("蓝色矩形");
System.out.println("形状: " + clonedShape2.getClass().getSimpleName());
clonedShape2.draw();
// 验证克隆的对象是新的实例
Shape clonedShape3 = ShapeCache.getShape("红色圆形");
System.out.println("clonedShape1 == clonedShape3: " + (clonedShape1 == clonedShape3)); // false
// 修改克隆对象不会影响原对象
if (clonedShape1 instanceof Circle) {
Circle circle1 = (Circle) clonedShape1;
circle1.setColor("绿色");
circle1.setRadius(15);
circle1.draw();
Circle circle3 = (Circle) clonedShape3;
circle3.draw(); // 仍然是红色的,半径为10
}
}
}
深拷贝与浅拷贝
浅拷贝示例
java
public class Book implements Cloneable {
private String title;
private Author author; // 引用类型
public Book(String title, Author author) {
this.title = title;
this.author = author;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
// Getter和Setter方法
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
public class Author {
private String name;
public Author(String name) {
this.name = name;
}
// Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Author author = new Author("张三");
Book book1 = new Book("Java设计模式", author);
// 浅拷贝
Book book2 = (Book) book1.clone();
// 修改拷贝对象的引用类型属性
book2.getAuthor().setName("李四");
// 原对象的引用类型属性也被修改了
System.out.println("book1的作者: " + book1.getAuthor().getName()); // 李四
System.out.println("book2的作者: " + book2.getAuthor().getName()); // 李四
}
}
深拷贝示例
java
public class Book implements Cloneable {
private String title;
private Author author; // 引用类型
public Book(String title, Author author) {
this.title = title;
this.author = author;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Book cloned = (Book) super.clone();
// 深拷贝,创建新的Author对象
cloned.author = new Author(this.author.getName());
return cloned;
}
// Getter和Setter方法
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
public class Author {
private String name;
public Author(String name) {
this.name = name;
}
// Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Author author = new Author("张三");
Book book1 = new Book("Java设计模式", author);
// 深拷贝
Book book2 = (Book) book1.clone();
// 修改拷贝对象的引用类型属性
book2.getAuthor().setName("李四");
// 原对象的引用类型属性没有被修改
System.out.println("book1的作者: " + book1.getAuthor().getName()); // 张三
System.out.println("book2的作者: " + book2.getAuthor().getName()); // 李四
}
}
使用序列化实现深拷贝
java
import java.io.*;
public class DeepCopyUtil {
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepCopy(T object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
T deepCopy = (T) ois.readObject();
ois.close();
return deepCopy;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}
public class Book implements Serializable {
private String title;
private Author author;
public Book(String title, Author author) {
this.title = title;
this.author = author;
}
// Getter和Setter方法
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
public class Author implements Serializable {
private String name;
public Author(String name) {
this.name = name;
}
// Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Client {
public static void main(String[] args) {
Author author = new Author("张三");
Book book1 = new Book("Java设计模式", author);
// 使用序列化实现深拷贝
Book book2 = DeepCopyUtil.deepCopy(book1);
// 修改拷贝对象的引用类型属性
book2.getAuthor().setName("李四");
// 原对象的引用类型属性没有被修改
System.out.println("book1的作者: " + book1.getAuthor().getName()); // 张三
System.out.println("book2的作者: " + book2.getAuthor().getName()); // 李四
}
}
优缺点
优点
- 性能提高,避免了new操作带来的开销
- 简化了对象的创建过程,特别是当创建对象过程比较复杂时
- 可以在运行时动态地添加和删除产品
- 可以在运行时改变产品的值或结构
- 减少了子类的构造,原型模式使用克隆方法生成实例,比使用new创建实例更加灵活
缺点
- 配合克隆方法需要考虑深拷贝和浅拷贝的问题
- 每个类必须配备一个克隆方法,这对全新的类来说不是很难,但对已有的类不一定很容易
- 必须实现Cloneable接口
相关模式
- 抽象工厂模式:抽象工厂模式可以用原型模式来存储和克隆产品
- 建造者模式:建造者模式关注的是复杂对象的构建过程,而原型模式关注的是对象的复制
- 单例模式:单例模式不能与原型模式一起使用,因为单例类不允许被克隆
实际应用
- Java中的Object.clone()方法
- Spring框架中的BeanUtils.copyProperties()
- 各种游戏中的角色复制
- 文档编辑器中的复制粘贴功能
- 数据库中的记录复制
注意事项
- 使用原型模式时,需要注意深拷贝和浅拷贝的区别
- 如果对象中包含不可序列化的对象,则不能使用序列化实现深拷贝
- 克隆方法不应该执行构造方法,所以构造方法中的初始化代码不会被执行
- 在多线程环境下使用原型模式需要注意线程安全问题