高效安全的设计模式——原型模式

原型模式基本介绍

所谓的原型模式是一个创造型的模式,既然叫做原型模式,即表明了该模式实现中有一个样板实例,客户端从这个样板对象中,复制出一个内部属性一致的对象进行使用,即clone()。通常当对象创建过于复杂,或者构造实例的时候较为耗时的情况下,我们使用原型模式,可以大量节约性能,提高程序效率。

原型模式UML图

class prototype

Client:客户端用户
Prototype:抽象类或者接口,声明具备clone能力
ConcretePrototype:具体的原型类

原型模式关键点

1)通过实现Clone able接口后在调用clone创建实例时并不是一定比new的操作速度快,只有在使用new构造对象较为耗时或者成本较高时,才会显出优势。
2)clone()方法并不是Cloneable接口中的的,而是Object中的方法,使用Cloneable是用来标明这个对象是可以被拷贝的。

代码示例

我们来看一段使用原型模式的代码


    static class School implements Cloneable {
        String schoolName;
        ArrayList<String> students = new ArrayList<String>();

        @Override
        protected School clone() {
            try {
                School s = (School) super.clone();
                s.schoolName = this.schoolName;
                s.students = this.students;
                return s;
            } catch (Exception e) {

            }
            return null;
        }

        @Override
        public String toString() {
            String studentsStr = "";
            for (String s :
                    students) {
                studentsStr += s + "  ";
            }
            return "Student:" + "SchoolName-" + this.schoolName + ",Student-" + studentsStr;
        }
    }

先建立了一个School类,并且继承了Cloneable接口,重写了clone方法。接下来看main();

    public static void main(String[] args) {
        School s1 = new School();
        s1.schoolName = "北京一中";
        s1.students.add("小明");
        School s2 = s1.clone();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println("---------------------修改s2----------------------");
        s2.schoolName = "北京二中";
        s2.students.remove("小明");
        s2.students.add("小芳");
        System.out.println(s1);
        System.out.println(s2);
    }

main函数显示创建了一个School对象s1,接着使用s1的clone方法克隆出了s2,我们先看输出结果。

这里写图片描述

在修改S2的之前,s1和s2的输出内容是一样的,说明s2成功通过s1的clone方法克隆了一个School的实例。
接着我们修改了s2中的schoolName以及students方法,我们发现s1中的school Name没有被修改,但是Students信息却被一起修改了。

因为在School的clone方法中,我们只是简单的引用了原本的值,当原型中的字段不是基本的值类型而是对象类型的时候,这样的引用只是指向了原型的对象的地址中。由于String schoolName是基础值类型,所以是将值直接赋给了s2中的schoolName,而对于students这样的对象,我们在clone方法中同样也需要调用他的clone方法,这就是原型模式中的 深拷贝与浅拷贝 问题,

浅拷贝
既简单的拷贝了原型对象的第一层结构。
深拷贝
对原型对象进行递归式的拷贝,将深层次的值也进行拷贝。

根据以上定义,表明我们需要修改School中的clone方法,students字段不能简单的使用等号来引用,而是使用他的clone方法来拷贝一份。
clone方法修改如下

        @Override
        protected School clone() {
            try {
                School s = (School) super.clone();
                s.schoolName = this.schoolName;
                s.students = (ArrayList<String>)this.students.clone();
                return s;
            } catch (Exception e) {

            }
            return null;
        }

运行结果如下

run main result2

可以看到s1中的Students并没有被修改。只是修改了拷贝出来的对象。

小结

使用原型模式可以直接做出对原型对象的拷贝,这样在处理复杂度较高的对象时有着较为明显的优势,并且可以通过对外开放拷贝出的副本对象,让外界只能修改副本,而不破坏原型自身,减少外界的影响给自身带来的Bug,即所谓的保护性拷贝
不过需要注意的是,当使用clone来创建对象的时候,是不会执行构造方法的,所以当使用原型模式时,需要特别留意一下构造方法中是否有必须要执行的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值