设计模式之原型模式

原型模式就是用已存在的对象实例,通过复制该实例生成一个相同或类似的对象。而且使用原型模式非常高效简便。

实现思路:在java下,所有的类的超类Object有提供对象的clone()方法,所以使用java实现这种原型模式还是比较简单的。涉及到的模型角色:

  • 1.抽象原型:由接口实现,继承至Cloneable接口,给出所有具体原型需要的接口,至少有一个clone 的方法;
  • 2.具体原型:继承抽象原型接口,实现其方法,返回被复制的对象;
  • 3.使用者:发起复制的类;

 以一个学生类作为栗子:

抽象原型:

public interface StudentPro extends Cloneable{

    StudentPro clone();
}

具体原型:

public class Student implements StudentPro {
    public String name;
    public int age;
    public int sex;
    public String address;

    @Override
    public StudentPro clone() {
        try {
            return (StudentPro) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String toString() {
        return "[" + name + ", " + age + ", " + sex + ", " + address + "]";
    }
}

使用: 

Student s1 = new Student();
        s1.address = "广东天河";
        s1.name = "哈哈";
        s1.sex = 1;
        s1.age = 2;
        Log.d("TAG", "s1: " + s1.toString());
        Student s2 = (Student) s1.clone();
        Log.d("TAG", "s2: " + s2.toString());

结果:

 D/TAG: s1: [哈哈, 2, 1, 广东天河]
 D/TAG: s2: [哈哈, 2, 1, 广东天河]

可以看到俩个对象属性的内容都是一样的。

原型模式的复制也是有分类的。根据复制的深度可分为深克隆跟浅克隆。浅克隆是克隆对象的基本类型的属性值跟引用类型的地址值。意思就是引用类型的对象使用的是同一个对象来的,并没有产生一个新的对象。而深克隆则是不管是基本类型还是引用类型统统都克隆,产生新的对象。修改前面的例子,验证一下:

新增一个类对象(同时修改toString方法):

public class Country {
    private String name;

    public String getName() {
        return name;
    }

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

在具体模型新增2个成员变量:

    private List likes;
    private Country country;

    public List getLikes() {
        return likes;
    }

    public void setLikes(List likes) {
        this.likes = likes;
    }

    public Country getCountry() {
        return country;
    }

    public void setCountry(Country country) {
        this.country = country;
    }

然后使用修改:

Student s1 = new Student();
        s1.address = "广东";
        s1.name = "哈哈";
        s1.sex = 1;
        s1.age = 2;
        List<String> list = new ArrayList<>();
        list.add("rap");
        list.add("跳");
        s1.setLikes(list);
        Country country = new Country();
        country.setName("XXC");
        s1.setCountry(country);
        Log.d("TAG", "s1: " + s1.toString()); //一开始的s1

        Student s2 = (Student) s1.clone();
        s2.getLikes().add("打篮球");
        s2.getCountry().setName("OOP");
        Log.d("TAG", "s2: " + s2.toString()); //s2
        Log.d("TAG", "s1: " + s1.toString());  //第二次 s1

结果:

 D/TAG: s1: [哈哈, 2, 1, 广东,XXC, [rap, 跳]]
 D/TAG: s2: [哈哈, 2, 1, 广东,OOP, [rap, 跳, 打篮球]]
 D/TAG: s1: [哈哈, 2, 1, 广东,OOP, [rap, 跳, 打篮球]]

看到结果,在对s2的修改同时也对s1生效了。所以 getLikes(),getCountry() 里拿到的是一个对象的引用,而不是一个新的对象。拿例子来说,想要克隆s2同时也克隆出一个新的likes,country对象就要用到深克隆了。

深克隆是利用序列化实现,实现Serializable接口。是所有的都要实现Serializable接口,比如引用里面的引用对象也要实现该接口,当然没有的话异常也是会提醒你的。

在抽象原型上修改,实现Serializable 接口,并增加一个深克隆的方法:

public interface StudentPro extends Cloneable,Serializable {

    StudentPro clone();

    StudentPro deepClone();

}

具体模型重写这方法:

/**
 * 通过读写流实现
 * @return
 */
@Override
    public StudentPro deepClone() {
        try {

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois;
            ois = new ObjectInputStream(bis);

            return (StudentPro) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

s1保持一样,改变s2的克隆:

...... 
Student s2 = (Student) s1.deepClone();
        s2.getLikes().add("打篮球");
        s2.getCountry().setName("OOP");

结果:

 D/TAG: s1: [哈哈, 2, 1, 广东, XXC, [rap, 跳]]
 D/TAG: s2: [哈哈, 2, 1, 广东, OOP, [rap, 跳, 打篮球]]
 D/TAG: s1: [哈哈, 2, 1, 广东, XXC, [rap, 跳]]

只有s2的值被修改了,s1没有被修改。

模式特点:

高效简便,性能优良。原型模式是在内存二进制流的复制,比直接new出一个对象性能要好。不需要做使用构造函数的准备工作,节省资源。同时这也是一个缺点,不走构造函数可能会导致准备工作不足。要根据所处环境选择。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值