为了账号安全,请及时绑定邮箱和手机立即绑定

设计模式----原型模式

标签:
Java

原型模式概念

原型模式是指通过复制一个已经存在的实例来返回新的实例,而不是新建实例,被复制的实例就是我们所称的原型对象。调用者不需要知道任何创建细节,既然是拷贝实例获取新的实例,因此不会直接调用构造方法

原型模式应用场景

初始化消耗资源较多

实例化一个对象需要繁琐的过程

构造函数比较复杂

循环体中产生大量对象时

Java中的浅克隆

下面介绍浅克隆,通过调用clone()方法实现复制要给对象的效果

  1. 编写一个顶层接口,里面定义一个克隆的方法
public interface Prototype {

    //定义一个克隆的方法
    Prototype clone();
}
  1. 编写一个Pets,作为宠物类,需要在Person类中作为引用对象被声明
public class Pets {
    //定义属性,记录宠物的属性
    private String kind;

    public String getKind() {
        return kind;
    }

    public void setKind(String kind) {
        this.kind = kind;
    }
}
  1. 编写一个实现顶层接口的实现类Person
public class Person implements Prototype{
    //定义属性
    private int age;
    private String name;
    //定义一个引用类型的对象,这里使用之前定义的宠物类
    private Pets pets;

    //提供get和set方法
    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public Pets getPets() {
        return pets;
    }

    public void setPets(Pets pets) {
        this.pets = pets;
    }

    //重写克隆的方法
    @Override
    public Prototype clone() {
        Person person = new Person();
        person.setAge(this.age);
        person.setName(this.name);
        person.setPets(this.pets);
        return person;
    }
}
  1. 模拟一个应用程序,假设应用程序需要使用到根据原型对象克隆而来的克隆对象。
public class Application {

    //这里是在模拟应用程序在测试类中调用克隆对象的方法
    public Prototype getPrototype(Prototype prototype){
        //调用接口中定义的克隆方法
        return prototype.clone();
    }
}
  1. 测试程序
public class Test {
    public static void main(String[] args) {
        //创建Person对象,作为原型对象
        Person p1 = new Person();
        p1.setAge(18);
        p1.setName("张三");
        //定义一个宠物类对象
        Pets pets = new Pets();
        pets.setKind("dog");
        p1.setPets(pets);

        //获取克隆对象
        Application app = new Application();
        Person p2 = (Person)app.getPrototype(p1);

        //将原型对象p1中的地址和克隆对象p2中的地址做比较
        System.out.println("原型对象p1中pets的地址:"+p1.getPets());
        System.out.println("克隆对象p2中pets的地址:"+p2.getPets());
        System.out.println("两者之间地址的比较:"+(p1.getPets()==p2.getPets()));
    }
}
  • 测试程序运行后的结果

image-20210606213103871

  • 结论:根据上面测试结果可以得出,这种方式获取的克隆对象中pets对象引用的地址是没有改变的,克隆对象只是使用了原型对象中引用对象的地址,并没有创建引用对象的地址。

  • 通过克隆对象修改属性值,再次验证上述结论

1)验证测试代码如下:

public class Test {
    public static void main(String[] args) {
        //创建Person对象,作为原型对象
        Person p1 = new Person();
        p1.setAge(18);
        p1.setName("张三");
        //定义一个宠物类对象
        Pets pets = new Pets();
        pets.setKind("dog");
        p1.setPets(pets);

        //获取克隆对象
        Application app = new Application();
        Person p2 = (Person)app.getPrototype(p1);

        //首先输出原型对象和克隆对象中pets引用中的kind的值
        System.out.println("原型对象:"+p1.getPets().getKind());
        System.out.println("克隆对象:"+p2.getPets().getKind());
        //通过克隆对象修改pets引用中的kind的值
        p2.getPets().setKind("cat");
        //再次输出原型对象和克隆对象中pets引用中的kind的值
        System.out.println("原型对象:"+p1.getPets().getKind());
        System.out.println("克隆对象:"+p2.getPets().getKind());
    }
}

2)测试结果

image-20210606213709663

  • 根据第二次测试结果可以得出,如果有引用对象,那么克隆对象是直接使用原型对象中的引用地址,并不会重新创建新的引用地址,因此,通过克隆对象修改了引用地址中的值之后,原型对象中的属性值也随之修改。

Java中的深克隆

  • 使用clone函数嵌套的方式比较简单,这里就不再使用,下面使用序列化的方式实现深克隆
  • 还是以上面浅克隆的事例代码演示

1)顶层接口设计

public interface Prototype {

    //定义一个克隆的方法
    Prototype clone();
}

2)定义一个宠物类,用于后续的Person中用于声明引用对象

public class Pets implements Serializable {

    //定义一个宠物种类属性
    private String kind;

    public String getKind() {
        return kind;
    }

    public void setKind(String kind) {
        this.kind = kind;
    }
}

3)定义Person类,需要该类实现序列化接口

public class Person implements Serializable,Prototype {
    private String name;
    private int age;
    //声明一个宠物类对象,引用类型
    private Pets pets;

    //get和set方法
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Pets getPets() {
        return pets;
    }

    public void setPets(Pets pets) {
        this.pets = pets;
    }

    //重写Prototype接口中的clone方法,并用序列化的方式实现
    @Override
    public Prototype clone() {
        //创建一个Person对象
        Person person = new Person();
        person.setAge(this.age);
        person.setName(this.name);
        person.setPets(this.pets);

        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            //序列化
            oos.writeObject(person);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            //反序列化
            return (Person)ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

4)创建一个应用程序,用于模拟其他应用程序需要Person对象,使用克隆的方式获取

public class Application {

    //这里是在模拟应用程序在测试类中调用克隆对象的方法
    public Prototype getPrototype(Prototype prototype){
        //调用接口中定义的克隆方法
        return prototype.clone();
    }
}

5)测试代码

public class Test {
    public static void main(String[] args) {
        //创建Person对象,作为原型对象
        Person p1 = new Person();
        p1.setName("张三");
        p1.setAge(11);
        Pets pets = new Pets();
        pets.setKind("dog");
        p1.setPets(pets);

        //假设application应用程序需要用到Person类,用克隆的方式获取
        Application app = new Application();
        Person p2 = (Person)app.getPrototype(p1);

        //获取原型对象p1和克隆对象p2中引用对象pets的地址
        System.out.println("原型对象:"+p1.getPets());
        System.out.println("克隆对象:"+p2.getPets());

        //输出原型对象p1和克隆对象p2中pets引用对象的kind属性
        System.out.println("原型对象:"+p1.getPets().getKind());
        System.out.println("克隆对象:"+p2.getPets().getKind());
        //使用克隆对象修改pets的属性值
        p2.getPets().setKind("cat");
        System.out.println("原型对象:"+p1.getPets().getKind());
        System.out.println("克隆对象:"+p2.getPets().getKind());
    }
}
  • 测试结果图

image-20210606223905164

原型模型的总结

  • 原型模型总结

虽然上面都是在讲解Java中的深克隆和浅克隆,原型模型的概念是由一个对象作为原型对象,由该对象复制出一个新的对象,称为克隆对象,这种方式就是原型模式,因此克隆是实现原型模式的方式,但是原型模式和单例模式是存在冲突的。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消