设计模式之原型模式

定义:
根据现有的对象进行克隆产生一个新的对象。当直接进行对象创建性能消耗比较大的情形之下,即可用使用原型模式。 比如在创建一对象的时候,需要通过操作数据库或者文件才能被创建。这种情况之下我们就可以对已有的对象进行缓存,然后在下次创建对象的时候返回,从而减少数据库或者文件的操作。

                                                                                   具体结构图(图片摘自百度百科)

                               

需要的角色:
客户端(Client):具体调用者类。
抽象的原型类(Prototype): 实现Cloneable接口。 方便扩展。
具体的原型类(ConcretePrototype):客服端需要的对象, 重写Objectclone()方法,进行对象的克隆。

使用场景:
    1、类初始化需要消耗很大的资源。
    2、对性能和安全有较高的要求。
    3、创建一个对象需要大量的数据准备和访问权限。
    4、对象可能被多个使用者修改。

优点:
    1、对于需要产生大量对象的场景, 使用clone大大提高性能。
    2、直接从内存中拷贝, 跳过了构造函数的约束。

缺点:
    1、必须实现Cloneable接口。
    2、对于已经封装好的类不容易实现。
    3、逃避了构造函数的约束。

使用示例:
    1、细胞分裂。
    2、游戏中大量相同对象(打飞机游戏中的子弹)。

注意事项:
    1、原型模式是通过直接在内存中进行对象克隆的,因此不会执行构造方法,而且访问权限也是无效的。原型模式和单例模式是冲突的。
    2、clone()方法只会对基本数据类型(包括String)进行拷贝, 对于数组、map、自定义对象等不会进行拷贝,如果需要,则需要进行深度拷贝(对这些数据另行进行拷贝)。
   3、必须实现Cloneable 接口,不然会抛出CloneNotSupportedException异常。
    4、clone()的访问权限为protected, 需要修改为public,不然在客户端没法调用。


示例代码:

具体原型类(这只是简单测试, 如果需要扩展,可以定义基类)

public class Mobile implements Cloneable {
    private static final String TAG  = "Clone";
    private String name;
    private String brand;

    public Mobile() {
        Log.i(TAG, "Mobile: 构造方法被调用啦");
    }

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

    public void setBrand(String brand) {
        this.brand = brand;
    }

    @Override
    public Mobile clone() throws CloneNotSupportedException {
        return (Mobile) super.clone();
    }

    @Override
    public String toString() {
        return "name = " + name + "   , brand = " + brand ;
    }
}
客户端测试代码:

Mobile mobile = new Mobile();
        mobile.setName("红米note4");
        mobile.setBrand("小米");

        try {
            Mobile mobile1 = mobile.clone();
            mobile1.setName("vivox9");
            mobile1.setBrand("vivo");

            Mobile mobile2 = mobile.clone();
            mobile2.setBrand("乐视");
            mobile2.setName("乐视s3");

            Log.i(TAG, "cloneMobile:  " + mobile.toString());
            Log.i(TAG, "cloneMobile:  " + mobile1.toString());
            Log.i(TAG, "cloneMobile:  " + mobile2.toString());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
测试结果: 从输出的结果可以看出, 构造方法值被执行一次,说明后面两个对象都是通过clone实现的,从而验证了clone不调用构造方法的说法。


上面示例代码中的对象的属性采用的是String, 下面我们换成自定义对象试试:

自定义属性类:

public class Brand  {
    private String name;
    private String sysVersion;

    public String getName() {
        return name;
    }

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

    public String getSysVersion() {
        return sysVersion;
    }

    public void setSysVersion(String sysVersion) {
        this.sysVersion = sysVersion;
    }

    @Override
    public String toString() {
        return "name = " + name + ",  sysVersion = " + sysVersion ;
    }
}
具体原型类中代码修改:

public class Mobile implements Cloneable {
    private static final String TAG  = "Clone";
    private String name;
    private Brand brand;

    public Mobile() {
        Log.i(TAG, "Mobile: 构造方法被调用啦");
        this.brand = new Brand();
    }

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

    public void setBrand(String name, String sysVersion) {
        this.brand.setName(name);
        this.brand.setSysVersion(sysVersion);
    }

    @Override
    public Mobile clone() throws CloneNotSupportedException {
        return (Mobile) super.clone();
    }

    @Override
    public String toString() {
        return "name = " + name + "   , brand = " + brand ;
    }
}
客户端测试代码:
 Mobile mobile = new Mobile();
        mobile.setName("红米note4");
        mobile.setBrand("小米", "6.0");

        try {
            Mobile mobile1 = mobile.clone();
            mobile1.setName("vivox9");
            mobile1.setBrand("vivo", "6.0");

            Mobile mobile2 = mobile.clone();
            mobile2.setBrand("乐视", "5.0");
            mobile2.setName("乐视s3");

            Log.i(TAG, "cloneMobile:  " + mobile.toString());
            Log.i(TAG, "cloneMobile:  " + mobile1.toString());
            Log.i(TAG, "cloneMobile:  " + mobile2.toString());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
测试结果:通过测试结果可以看出, 自定义属性并没有被clone,只是后面的赋值替换前面的赋值,从而以验证了clone()方法只能对基本数据类型以及String类型进行clone,而不能对数组、map、自定义数据类型进行clone(这就是常说的浅克隆)。

那怎样能实现对自定义数据类型进行拷贝呢? 实现很简单,进行深度克隆即可,即对自定义数据类型进行单独克隆,示例代码如下:

自定义数据类: 需要实现Cloneable接口, 重写clone()方法。

public class Brand implements Cloneable {
    private String name;
    private String sysVersion;

    public String getName() {
        return name;
    }

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

    public String getSysVersion() {
        return sysVersion;
    }

    public void setSysVersion(String sysVersion) {
        this.sysVersion = sysVersion;
    }

    @Override
    public Brand clone() throws CloneNotSupportedException {
        return (Brand) super.clone();
    }

    @Override
    public String toString() {
        return "name = " + name + ",  sysVersion = " + sysVersion ;
    }
}
具体原型类中代码

public class Mobile implements Cloneable {
    private static final String TAG  = "Clone";
    private String name;
    private Brand brand;

    public Mobile() {
        Log.i(TAG, "Mobile: 构造方法被调用啦");
        this.brand = new Brand();
    }

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

    public void setBrand(String name, String sysVersion) {
        this.brand.setName(name);
        this.brand.setSysVersion(sysVersion);
    }

    @Override
    public Mobile clone() throws CloneNotSupportedException {
        Mobile mobile = (Mobile) super.clone();
        mobile.brand = this.brand.clone(); //对引用对象进行深度clone
        return mobile;
    }

    @Override
    public String toString() {
        return "name = " + name + "   , brand = " + brand ;
    }
}
客户端测试代码:

 Mobile mobile = new Mobile();
        mobile.setName("红米note4");
        mobile.setBrand("小米", "6.0");

        try {
            Mobile mobile1 = mobile.clone();
            mobile1.setName("vivox9");
            mobile1.setBrand("vivo", "6.0");

            Mobile mobile2 = mobile.clone();
            mobile2.setBrand("乐视", "5.0");
            mobile2.setName("乐视s3");

            Log.i(TAG, "cloneMobile:  " + mobile.toString());
            Log.i(TAG, "cloneMobile:  " + mobile1.toString());
            Log.i(TAG, "cloneMobile:  " + mobile2.toString());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
测试结果:从测试结果看出,我们实现了深度克隆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值