# 原型模式(Prototype Pattern)
代码地址
- Github:DesignPattern/src/main/java/com/design/prototype (opens new window)
- Gitee(码云):DesignPattern/src/main/java/com/design/prototype (opens new window)
# 1. 介绍
原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
原型实例指定创建对象的种类,通过拷贝这些原型创建新的对象,这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式
# 1.1. 特点
待补充
# 1.2. 结构
待补充
# 1.3. 优缺点
- 意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
- 主要解决:在运行期建立和删除原型
- 如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例
- 关键代码:
- 实现克隆操作,在 JAVA 实现 Cloneable 接口,重写 clone()
- 在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝
- 使用场景: 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用
- 优点:
- 性能提高
- 逃避构造函数的约束
- 缺点:
- 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候
- 必须实现 Cloneable 接口
- 应用场景:
- 细胞分裂
- JAVA 中的 Object clone() 方法
- 注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流
# 2. 代码
使用一个人有姓名,年龄,地址属性的复制拷贝例子,理解浅拷贝和深拷贝
/**
* 人,姓名,年龄,地址
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/1 17:32
*/
public class Person implements Cloneable {
private String name;
private int age;
private Location location;
public Person(String name, int age, Location location) {
this.name = name;
this.age = age;
this.location = location;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
// 基础属性String及int可以不用处理
// 内置引用不重新进行克隆是浅拷贝,会导致指向同一个Location
// 必须针对内部引用再次进行克隆,这样才是深拷贝
person.setLocation((Location) location.clone());
return person;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", location=" + location +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
}
/**
* 地址
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/1 17:32
*/
public class Location implements Cloneable {
private StringBuffer address;
private String remark;
public Location(StringBuffer address, String remark) {
this.address = address;
this.remark = remark;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// String基础属性可以不用处理,但是StringBuffer等引用对象需要处理
Location location = (Location) super.clone();
location.setAddress(new StringBuffer(address));
return location;
}
@Override
public String toString() {
return "Location{" +
"address=" + address +
", remark='" + remark + '\'' +
'}';
}
public StringBuffer getAddress() {
return address;
}
public void setAddress(StringBuffer address) {
this.address = address;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
/**
* 原型模式 - 克隆,拷贝
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/1 17:32
*/
public class Main {
public static void main(String[] args) throws Exception {
StringBuffer address = new StringBuffer("深圳市南山区XXX");
Location location = new Location(address, "XXX村");
Person wangMing = new Person("李明", 20, location);
// 克隆wangMing
Person liMing = (Person) wangMing.clone();
// 修改原始对象
wangMing.setName("王明");
wangMing.setAge(30);
address.reverse();
location.setRemark("XXX路");
// 输出原始和克隆对象,正常应该是深拷贝
// 修改原始对象,克隆对象还是刚开始声明的属性
// 将Person重写的clone方法对Location的克隆注释放开
// 以及Location重写的clone方法对StringBuffer的克隆注释放开
// 不然就会出现浅拷贝的问题
System.out.println(wangMing);
System.out.println(liMing);
}
}
Person{name='王明', age=30, location=Location{address=XXX区山南市圳深, remark='XXX路'}}
Person{name='李明', age=20, location=Location{address=深圳市南山区XXX, remark='XXX村'}}