Java设计模式之原型模式

原型模式(Prototype Pattern)是一种创建型设计模式,允许你复制现有对象而不必深入理解其复杂结构。它通过复制原型对象来创建新对象,新对象是原型对象的一个精确副本,包括其内部状态。

概念与作用

原型模式的核心概念是“原型”,即一个具有某种特定状态的对象,通过复制这个对象来创建新的对象。

其作用主要体现在以下几个方面:

1.简化对象创建过程

无需通过new关键字以及一系列初始化操作来创建复杂对象,只需通过复制已有对象即可得到一个新对象,避免了繁琐的对象创建过程。

2.性能优化

在某些情况下,创建新对象的成本较高,如需要进行大量的计算或数据访问等。而原型模式通过复制已有对象,可以避免这些高成本的操作,提高系统的性能。

3.灵活的对象创建方式

用户可以根据实际需求动态地指定要创建的对象类型,通过选择不同的原型对象进行复制,实现灵活的对象创建。

举例

场景:简历创建系统

假设我们有一个简历创建系统,用户可以创建自己的简历,并且希望能够快速地复制一份简历作为基础,然后进行修改。这里就可以使用原型模式。

// 简历类,实现Cloneable接口
class Resume implements Cloneable {
    private String name; // 姓名
    private String gender; // 性别
    private int age; // 年龄
    private WorkExperience workExperience; // 工作经历

    // 构造方法
    public Resume(String name, String gender, int age, WorkExperience workExperience) {
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.workExperience = workExperience;
    }

    // 实现clone方法,深拷贝
    @Override
    protected Resume clone() {
        // 深拷贝:复制工作经历对象
        WorkExperience clonedWorkExperience = new WorkExperience(this.workExperience.getCompany(), this.workExperience.getWorkYears());
        return new Resume(this.name, this.gender, this.age, clonedWorkExperience);
    }

    // getter和setter方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public WorkExperience getWorkExperience() {
        return workExperience;
    }

    public void setWorkExperience(WorkExperience workExperience) {
        this.workExperience = workExperience;
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                ", workExperience=" + workExperience +
                '}';
    }
}
// 工作经历类
class WorkExperience {
    private String company; // 公司名称
    private int workYears; // 工作年限

    // 构造方法
    public WorkExperience(String company, int workYears) {
        this.company = company;
        this.workYears = workYears;
    }

    // getter和setter方法
    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public int getWorkYears() {
        return workYears;
    }

    public void setWorkYears(int workYears) {
        this.workYears = workYears;
    }

    @Override
    public String toString() {
        return "WorkExperience{" +
                "company='" + company + '\'' +
                ", workYears=" + workYears +
                '}';
    }
}
// 测试类
public class PrototypePatternDemo {
    public static void main(String[] args) {
        // 创建原始简历对象
        WorkExperience workExperience = new WorkExperience("ABC公司", 3);
        Resume originalResume = new Resume("张三", "男", 28, workExperience);

        System.out.println("原始简历:" + originalResume);

        // 复制简历对象,这里是直接使用已存在的对象,而不是新建对象
        Resume clonedResume = originalResume.clone();
        clonedResume.setName("李四");
        clonedResume.getWorkExperience().setCompany("DEF公司");

        System.out.println("复制后的简历:" + clonedResume);
    }
}
原始简历:Resume{name='张三', gender='男', age=28, workExperience=WorkExperience{company='ABC公司', workYears=3}}
复制后的简历:Resume{name='李四', gender='男', age=28, workExperience=WorkExperience{company='DEF公司', workYears=3}}

不使用原型模式的短板

如果不使用原型模式,直接通过new关键字创建对象,可能会存在以下问题:

1.重复初始化问题

如果对象的创建过程比较复杂,如需要进行大量的计算或数据访问等,那么每次创建新都需要对象重复这些初始化操作,导致系统性能下降。

2.无法灵活创建对象

无法根据现有对象的状态快速创建新对象,需要重新指定所有属性的值,不够灵活。

3.代码可维护性差

如果对象的结构发生变化,如增加新的属性或修改属性的类型等,那么所有创建该对象的地方都需要进行相应的修改,增加了代码的维护成本。

例如,在上述简历创建系统的场景中,如果不使用原型模式,每次创建新简历都需要重新输入姓名、性别、年龄以及工作经历等信息,无法快速地基于已有简历进行修改。而且,如果简历的结构发生变化,如增加教育背景等属性,那么所有创建简历的地方都需要修改,增加了代码的维护难度。

实现条件

在Java中,使用原型模式时,通常需要满足以下条件:

1.实现Cloneable接口

Cloneable是一个标记接口,没有需要实现的方法。但它用于表明该类的对象可以被复制。如果一个类没有实现Cloneable接口,但却调用了clone()方法,会抛出CloneNotSupportedException异常。

2. 重写clone()方法

clone()方法是Object类的一个protected方法。为了能够在外部分使用并定制复制逻辑,通常需要将其重写为public方法,并在其中实现具体的复制逻辑。

3. 深拷贝与浅拷贝的考虑

如果对象包含其他对象的引用(即组合对象),则需要考虑是进行浅拷贝还是深拷贝:

浅拷贝:只复制对象本身,不会复制其内部引用的对象,复制后的对象和原对象仍然引用相同的内部对象。

深拷贝:不仅复制对象本身,还会复制其内部引用的所有对象,确保复制后的对象与原对象完全独立。

在实际应用中,需要根据具体需求选择合适的拷贝方式。

深拷贝与浅拷贝

在对象复制过程中,浅拷贝和深拷贝是两种不同的复制方式,它们在处理对象的内部引用时有着本质的区别。

浅拷贝

浅拷贝只复制对象本身,不会复制其内部引用的对象。复制后的对象与原对象仍然引用相同的内部对象。

class Address {
    private String street;
    private String city;

    public Address(String street, String city) {
        this.street = street;
        this.city = city;
    }

    // getter和setter方法
    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Address{" +
                "street='" + street + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}
class Person implements Cloneable {
    private String name;
    private int age;
    private Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // 实现clone方法,浅拷贝
    @Override
    protected Person clone() {
        return new Person(this.name, this.age, this.address);
    }

    // getter和setter方法
    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 Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}
public class ShallowCopyDemo {
    public static void main(String[] args) {
        // 创建原始对象
        Address originalAddress = new Address("Main Street", "New York");
        Person originalPerson = new Person("Alice", 25, originalAddress);

        System.out.println("原始对象:" + originalPerson);

        // 浅拷贝
        Person clonedPerson = originalPerson.clone();

        System.out.println("浅拷贝后的对象:" + clonedPerson);

        // 修改原始对象的地址
        originalPerson.getAddress().setStreet("Broadway");

        System.out.println("修改原始对象地址后,原始对象:" + originalPerson);
        System.out.println("修改原始对象地址后,拷贝对象:" + clonedPerson);
    }
}
原始对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}
浅拷贝后的对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}
修改原始对象地址后,原始对象:Person{name='Alice', age=25, address=Address{street='Broadway', city='New York'}}
修改原始对象地址后,拷贝对象:Person{name='Alice', age=25, address=Address{street='Broadway', city='New York'}}

从输出结果可以看出,浅拷贝后的对象与原始对象共享相同的Address对象。当修改原始对象的地址时,拷贝对象的地址也随之改变。

深拷贝

深拷贝不仅复制对象本身,还会复制其内部引用的所有对象,确保复制后的对象与原对象完全独立。

class Address {
    private String street;
    private String city;

    public Address(String street, String city) {
        this.street = street;
        this.city = city;
    }

    // 深拷贝方法(浅拷贝没有这个方法)
    public Address deepCopy() {
        return new Address(this.street, this.city);
    }

    // getter和setter方法
    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Address{" +
                "street='" + street + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}
class Person implements Cloneable {
    private String name;
    private int age;
    private Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // 实现clone方法,深拷贝
    @Override
    protected Person clone() {
        // 深拷贝地址对象,浅拷贝没有这个
        Address clonedAddress = this.address.deepCopy();
        return new Person(this.name, this.age, clonedAddress);
    }

    // getter和setter方法
    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 Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}
public class DeepCopyDemo {
    public static void main(String[] args) {
        // 创建原始对象
        Address originalAddress = new Address("Main Street", "New York");
        Person originalPerson = new Person("Alice", 25, originalAddress);

        System.out.println("原始对象:" + originalPerson);

        // 深拷贝
        Person clonedPerson = originalPerson.clone();

        System.out.println("深拷贝后的对象:" + clonedPerson);

        // 修改原始对象的地址
        originalPerson.getAddress().setStreet("Broadway");

        System.out.println("修改原始对象地址后,原始对象:" + originalPerson);
        System.out.println("修改原始对象地址后,拷贝对象:" + clonedPerson);
    }
}
原始对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}
深拷贝后的对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}
修改原始对象地址后,原始对象:Person{name='Alice', age=25, address=Address{street='Broadway', city='New York'}}
修改原始对象地址后,拷贝对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}

从输出结果可以看出,深拷贝后的对象与原始对象是完全独立的。当修改原始对象的地址时,拷贝对象的地址保持不变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值