原型模式 Prototype Pattern
- 属于创建型模式
- 是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,不调用构造函数
- 原型模式的核心在于实现一个
clone方法,这个方法可以复制对象的状态,从而创建一个新的对象。新对象与原型对象具有相同的状态,但它们是两个独立的对象
原型模式通常包含以下几个角色:
- Prototype(抽象原型类) :声明一个克隆自身的接口。通常是一个抽象类或接口,定义了
clone方法。 - ConcretePrototype(具体原型类) :实现
clone方法,用于复制自身。具体的原型类,实现抽象原型类中定义的克隆方法。 - Client(客户端) :使用原型对象创建新的对象。客户端代码,通过调用原型对象的克隆方法来创建新的对象。
业务
类图
classDiagram
class Shape {
<<abstract>>
-x: int
-y: int
+clone(): Shape
+setPosition(int, int): void
+draw(): void
}
class Circle {
-radius: int
+Circle(int, int, int)
+clone(): Shape
+draw(): void
}
class Rectangle {
-width: int
-height: int
+Rectangle(int, int, int, int)
+clone(): Shape
+draw(): void
}
Shape <|-- Circle
Shape <|-- Rectangle
代码
假设我们正在开发一个图形绘制软件,其中有各种图形对象,如圆形、矩形、三角形等。当用户需要创建多个相似的图形时,可以使用原型模式。
- 首先定义一个抽象图形类(抽象原型类):
abstract class Shape {
protected int x;
protected int y;
public abstract Shape clone();
public void setPosition(int x, int y) {
this.x = x;
this.y = y;
}
public abstract void draw();
}
- 具体的圆形类(具体原型类):
class Circle extends Shape {
private int radius;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
@Override
public Shape clone() {
return new Circle(this.x, this.y, this.radius);
}
@Override
public void draw() {
System.out.println("Drawing a circle at (" + x + ", " + y + ") with radius " + radius);
}
}
- 具体的矩形类(具体原型类):
class Rectangle extends Shape {
private int width;
private int height;
public Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
@Override
public Shape clone() {
return new Rectangle(this.x, this.y, this.width, this.height);
}
@Override
public void draw() {
System.out.println("Drawing a rectangle at (" + x + ", " + y + ") with width " + width + " and height " + height);
}
}
- 客户端使用:
public class Main {
public static void main(String[] args) {
Circle originalCircle = new Circle(10, 10, 5);
Circle clonedCircle = (Circle) originalCircle.clone();
clonedCircle.setPosition(20, 20);
originalCircle.draw();
clonedCircle.draw();
Rectangle originalRectangle = new Rectangle(30, 30, 8, 6);
Rectangle clonedRectangle = (Rectangle) originalRectangle.clone();
clonedRectangle.setPosition(40, 40);
originalRectangle.draw();
clonedRectangle.draw();
}
}
在这个场景中,原型模式使得创建相似图形对象变得非常方便。当用户需要创建新的图形时,只需要复制现有的图形对象,然后进行一些修改即可,而不需要重新创建一个全新的对象。
在框架中的使用
java: 实现克隆方法
通过硬编码复制对象
编写比较麻烦
@Data
public class User {
private Long id;
private String name;
private String pwd;
/**
* 硬编码复制
*/
public User copy() {
User user = new User();
user.setId(this.getId());
user.setName(this.getName());
return user;
}
}
通过反射复制对象
本质也是 set、get
public static Object copy(Object prototype) {
Class<?> clazz = prototype.getClass();
Object returnValue;
try {
returnValue = clazz.newInstance();
// 获取所有属性, 并设置可访问
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
field.set(returnValue, field.get(prototype));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return returnValue;
}
浅拷贝
浅拷贝的特点
- 对于基本类型是按值复制,不会因为原对象的基本类型值的改变而影响到拷贝后的对象
- 对于引用类型,prototype 和 clone 对象引用同一个引用类型对象(复制引用对象的内存地址)
浅拷贝存在的问题
- 修改 clone[prototype] 引用的对象后,会影响到 prototype[clone]
@Data
public class ConcretePrototype implements Cloneable {
private String name;
private int age;
private List<String> hobbies;
/**
* 实现 Cloneable 接口, 底层使用字节码进行克隆
*/
@Override
public ConcretePrototype clone() {
try {
// TODO: copy mutable state here, so the clone can't change the internals of the original
return (ConcretePrototype) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
流实现深拷贝
@Data
public class DeepConcretePrototype implements Serializable
, Cloneable {
private String name;
private int age;
private List<String> hobbies;
@Override
public DeepConcretePrototype clone(){
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (DeepConcretePrototype)ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
json字符串实现深拷贝
假设我们有一个简单的类Person:
@Data
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
使用 JSON 进行深拷贝的方法如下:
import com.alibaba.fastjson.JSON;
public class DeepCopyUsingJSON {
public static void main(String[] args) {
Person person1 = new Person("Alice", 30);
String jsonStr = JSON.toJSONString(person1);
Person person2 = JSON.parseObject(jsonStr, Person.class);
person2.setName("Bob");
person2.setAge(25);
System.out.println("Person 1: " + person1.getName() + ", " + person1.getAge());
System.out.println("Person 2: " + person2.getName() + ", " + person2.getAge());
}
}
在这个例子中,首先将对象转换为 JSON 字符串,然后再从 JSON 字符串解析回对象,这样就实现了一个深拷贝的效果。新的对象和原对象是完全独立的,修改其中一个不会影响另一个。
spring
bean的单例、原型
总结
防止单例被原型模式破坏
1、单例类不实现 cloneable 接口 2、实现 cloneable,但是 clone 方法直接返回单例对象
使用场景
- 类初始化消耗资源较多
- new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂
- 循环体中生产大量对象
优点
- 性能优良,Java 自带的原型模式是基于二进制流的拷贝,比直接 new 一个对象性能上提升了许多
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建过程
缺点
- 必须匹配克隆方法
- 当对已有类进行改造的时候,需要修改代码,违反了开闭原则
- 深拷贝、浅拷贝需要运用得当