# 原型模式(Prototype Pattern)

目录: https://note.dolyw.com/design/ (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村'}}
上次更新时间: 2023-12-15 03:14:55