设计模式之原型模式

来源:互联网 发布:婚纱照排版台词 知乎 编辑:程序博客网 时间:2024/06/07 21:56
定义:
根据现有的对象进行克隆产生一个新的对象。当直接进行对象创建性能消耗比较大的情形之下,即可用使用原型模式。 比如在创建一对象的时候,需要通过操作数据库或者文件才能被创建。这种情况之下我们就可以对已有的对象进行缓存,然后在下次创建对象的时候返回,从而减少数据库或者文件的操作。

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

                               

需要的角色:客户端(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();        }
测试结果:从测试结果看出,我们实现了深度克隆。

原创粉丝点击