原型模式

/ 设计模式

原型模式

通过复制实例原型,生成新的实例对象。

GoF23种设计模式中,属于创建型模式( Creational patterns)

我们在new一个新对象时的成本是不低的。且当我们在创建一个复杂的对象,并需要多次创建时,对系统的开销更高。

原型模式正是解决这种问题的良方。

实现

原型模式实现很简单,只要实现Cloneable接口即可。

public class People implements Cloneable {

    /**
     * 基础数据类型
     */
    private String name;
    /**
     * 引用数据类型
     */
    private Secret secret;


    public static class Secret {

        private int age;

        public Secret(int age) {
            this.age = age;
        }

        public int getAge() {
            return age;
        }

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

        @Override
        public String toString() {
            return "Secret{" +
                    "age=" + age +
                    '}' + super.toString();
        }
    }

    public People(String name, Secret secret) {
        this.name = name;
        this.secret = secret;
    }

    public String getName() {
        return name;
    }

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

    public Secret getSecret() {
        return secret;
    }

    public void setSecret(Secret secret) {
        this.secret = secret;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", secret=" + secret +
                '}' + super.toString();
    }
}
public class Test {

    public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        //对18号进行克隆
        People x18 = new People("x战警18号",new People.Secret(18));
        People x19 = (People) x18.clone();
        System.out.println(x18);
        System.out.println(x19);

        //重命名
        x19.setName("x战警19号");
        x19.getSecret().setAge(19);
        System.out.println(x18);
        System.out.println(x19);
    }
}
//对18号进行克隆
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@72ea2f77
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@33c7353a

//重命名
People{name='x战警18号', secret=Secret{age=19}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@72ea2f77
People{name='x战警19号', secret=Secret{age=19}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@33c7353a

这里我们可以看到。

因此,我们把上面的拷贝过程称为浅拷贝

这是原型模式在使用时需要注意的地方。如果我们要实现克隆一个完全新的对象,我就需要实现深拷贝

深拷贝与浅拷贝

浅拷贝:仅拷贝对象的属性值。因此上面发生的显现就不难理解,对于引用型的Secret对象,在拷贝时仅是拷贝了该对象的引用地址。故而我们改变引用地址的值时,所有该地址的引用值都会改变。

深拷贝:在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝

具体操作就是需要该对象的引用类型对象也要实现 Cloneable接口,在对象进行克隆时,对引用类型对象进行单独的克隆赋值。

public class People implements Cloneable {

    /**
     * 基础数据类型
     */
    private String name;
    /**
     * 引用数据类型
     */
    private Secret secret;


    public static class Secret implements Cloneable{

        private int age;

        public Secret(int age) {
            this.age = age;
        }

        public int getAge() {
            return age;
        }

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

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }

        @Override
        public String toString() {
            return "Secret{" +
                    "age=" + age +
                    '}' + super.toString();
        }
    }

    public People(String name, Secret secret) {
        this.name = name;
        this.secret = secret;
    }

    public String getName() {
        return name;
    }

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

    public Secret getSecret() {
        return secret;
    }

    public void setSecret(Secret secret) {
        this.secret = secret;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        People people = (People)super.clone();
        people.secret = (Secret) secret.clone();
        return people;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", secret=" + secret +
                '}' + super.toString();
    }
}
public class Test {

    public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        //对18号进行克隆
        People x18 = new People("x战警18号",new People.Secret(18));
        People x19 = (People) x18.clone();
        System.out.println(x18);
        System.out.println(x19);

        //重命名
        x19.setName("x战警19号");
        x19.getSecret().setAge(19);
        System.out.println(x18);
        System.out.println(x19);
    }
}
//对18号进行克隆
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@72ea2f77
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@33c7353a}top.ordin.tool.design.pattern.creational.prototype.People@681a9515

//重命名
People{name='x战警18号', secret=Secret{age=18}top.ordin.tool.design.pattern.creational.prototype.People$Secret@5b2133b1}top.ordin.tool.design.pattern.creational.prototype.People@72ea2f77
People{name='x战警19号', secret=Secret{age=19}top.ordin.tool.design.pattern.creational.prototype.People$Secret@33c7353a}top.ordin.tool.design.pattern.creational.prototype.People@681a9515

总结

原型模式实现起来虽然很简单,但是一定要注意复制的对象是否符合预期。是拷贝了对象的值还是仅仅拷贝了对象的引用,也就是深克隆和浅克隆的合理运用。