23种设计模式 —— 原型模式【克隆羊、浅拷贝、深拷贝】

简介: 23种设计模式 —— 原型模式【克隆羊、浅拷贝、深拷贝】

前言

前面学习了工厂模式,今天给大家带来另一种Java设计模式:原型设计模式。这个模式较比于工厂模式,我用克隆羊的Java案例来进行讲解,就比较容易理解和使用,内容不多,希望大家喜欢💕

前提引用

假设有一只羊,叫做“多莉”(就是高中学的那个克隆羊多莉),年龄是3岁,颜色是白色。现在用编程实现对多莉的克隆:即克隆一只跟它一模一样的小羊(名字、年龄和颜色相同)

💨解决方式

一、🍂传统方式

🔥设计代码

先创建多莉这个小羊:

package java设计模式.prototype;

public class Sheep {
    private String name;

    private int age;

    private String color;

    public Sheep(String name, int age, String color) {

        this.name = name;

        this.age = age;

        this.color = color;

    }

    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 String getColor() {

        return color;

    }

    public void setColor(String color) {

        this.color = color;

    }

    @Override

    public String toString() {

        return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";

    }

}

创建类Client当做生物学家,操作克隆的整个过程

package java设计模式.prototype;

public class Client {
    public static void main(String[] args) {
        //原型小羊

        Sheep oldSheep = new Sheep("多莉", 3, "白色");

        //开始克隆

        Sheep newSheep = new Sheep(oldSheep.getName(), oldSheep.getAge(), oldSheep.getColor());

        System.out.println(oldSheep);

        System.out.println(newSheep);
    }
}

是如何操作的呢?由代码可以看出,当我们要克隆一个新的小羊的时候,我们在构造器中直接引用了原型小羊的getAge()getName()getColor()的方法,实现了全部的克隆。

运行结果 image.png

🔥优缺点比较

**优点:**

优点是比较好理解,简单易操作

**缺点:**

在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低

总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活

二、🍂原型模式

🔥思路分析

那么,该如何避免传统方式的缺点呢?

我们知道,Java中Object类是所有类的父类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力,由此来引出我们的原型模式。

🔥基本介绍

• 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
• 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象, 无需知道如何创建的细节
• `工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建
的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()`

🔥类图分析

image.png

原理结构说明:
Prototype:原型类,声明一个克隆自己的接口
ConcreteProtptype:具体的原型类,实现一个克隆自己的操作
Client:让一个原型对象克隆自己,从而创建一个新对象(属性一样)

🔥代码设计

让上面的Sheep类实现Cloneable接口后重写clone()方法,代码如下:

package java设计模式.prototype.improve;

public class Sheep implements Cloneable{

        private String name;

        private int age;

        private String color;

    public Sheep(String name, int age, String color) {

            this.name = name;

            this.age = age;

            this.color = color;

        }

        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 String getColor() {

            return color;

        }
        //克隆该实例,使用默认的clone方法完成

        @Override
        protected Object clone()  {
            Sheep sheep = null;

            try {

                sheep= (Sheep) super.clone();

            } catch (Exception e) {

                System.out.println(e.getMessage());

            }

            return sheep;
        }

        public void setColor(String color) {

            this.color = color;

        }

        @Override

        public String toString() {

            return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";

        }

    }

最后建一个Client类,用来测试:

package java设计模式.prototype.improve;

public class Client {
    public static void main(String[] args) {
        Sheep oldSheep = new Sheep("多莉", 3, "白色");
        Sheep sheep1 = (Sheep) oldSheep.clone();
        Sheep sheep2 = (Sheep) oldSheep.clone();
        System.out.println(sheep1);
        System.out.println(sheep2);
    }
}

image.png

💨方式比较

<font color=#AAA00>那么小伙伴们可能就要问了,这不跟方式一一样吗?没什么简便的地方啊,那么问题来了,假如那只多莉小羊来自于北京,我的克隆羊也必须来自于北京,用方式一的办法,是不是还需要从构造器中手动创建?如果要克隆一百只,一万只,一千万只小羊呢?用方式一是不是很麻烦!而用方式二,设定好需要克隆的小羊后,只需要使用clone()方法就可以一键克隆小羊了,再多的小羊,也不怕!

原型模式在Spring中的源码分析


浅拷贝

image.png

image.png

image.png

image.png

image.png

深拷贝

image.png

public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String color;
    private String address="内蒙古";
    private Sheep friend;
    //....
}

public class Client {
    public static void main(String[] args) {
        Sheep sheep1 = new Sheep("Tom", 3, "白色");
        sheep1.setFriend(new Sheep("Bob", 2, "黑色"));
        Sheep sheep2 = (Sheep) sheep1.clone();
        Sheep sheep3 = (Sheep) sheep1.clone();
        //...
        System.out.println(sheep1.getFriend().hashCode());
        System.out.println(sheep2.getFriend().hashCode());
        System.out.println(sheep3.getFriend().hashCode());
    }
}

方式一:重新clone()方法(这里懒得写set、get了,就直接把变量声明为public)

package design_partten.prototype.type1.improve;

class Bird implements Cloneable{
    public String name;

    public Bird(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Sheep implements Cloneable {
    public String name;
    public int age;
    public String color;
    public Bird friend;

    public Sheep(String name, int age, String color, Bird friend) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.friend = friend;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {

        //1. 先拷贝基本数据类型的成员变量
        Sheep sheep = (Sheep) super.clone();

        //2. 拷贝引用类型的成员变量(即对引用类型成员再进行一次拷贝,然后赋值给刚刚拷贝的克隆羊)
        Bird friend = (Bird) this.friend.clone();
        sheep.friend = friend;

        return sheep;
    }
}

测试:

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep1 = new Sheep("Tom", 3, "白色", new Bird("鸟"));
        Sheep sheep2 = (Sheep) sheep1.clone();
        Sheep sheep3 = (Sheep) sheep1.clone();
        //...
        System.out.println(sheep1.friend.hashCode());
        System.out.println(sheep2.friend.hashCode());
        System.out.println(sheep3.friend.hashCode());
    }
}

结果:

评价:通过重新clone()方法来实现深拷贝,虽然看上去简单,代码较少,但这是因为我们成员变量只有一个是引用类型,如果有很多成员变量都是引用类型,那么我们每个变量都要去写。不推荐。

方式二:通过对象的序列化方式实现(推荐)

//在Sheep类添加该方法,且Sheep和Bird实现Serialized接口
public Object deepClone(){
    //创建字节数组输出流对象
    ByteArrayOutputStream bos = null;
    ObjectOutputStream oos = null;
    ByteArrayInputStream bis = null;
    ObjectInputStream ois = null;

    try {
        //序列化
        bos = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(bos);
        oos.writeObject(this);//当前这个对象以对象流的方式输出

        //反序列化
        bis = new ByteArrayInputStream(bos.toByteArray());
        ois = new ObjectInputStream(bis);
        Sheep sheep = (Sheep)ois.readObject();

        return sheep;
    }catch (Exception e){
        e.getStackTrace();
        return null;
    }finally {
        //关闭流
        try {
            ois.close();
            bis.close();
            oos.close();
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

📕总结

创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率

不用重新初始化对象,而是动态地获得对象运行时的状态

如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码

在实现深克隆的时候可能需要比较复杂的代码

缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则,这点需要特别注意.

相关实践学习
简单用户画像分析
本场景主要介绍基于海量日志数据进行简单用户画像分析为背景,如何通过使用DataWorks完成数据采集 、加工数据、配置数据质量监控和数据可视化展现等任务。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
相关文章
|
1月前
|
设计模式 安全 Java
【设计模式】原型模式
【设计模式】原型模式
|
15天前
|
设计模式 Java
小谈设计模式(10)—原型模式
小谈设计模式(10)—原型模式
|
1月前
|
设计模式 Java
设计模式之原型模式
设计模式之原型模式
|
3月前
|
设计模式 存储 JSON
Java设计模式-原型模式
原型模式也是创建对象的一种方式,它一般用在这样的场景:系统中存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂而且耗费资源。这个时候使用原型模式的克隆方式,能够节省不少时间。比如Java 类中提供的`Object clone()`就是原型模式的应用。
30 1
Java设计模式-原型模式
|
3月前
|
设计模式
设计模式 | 原型模式
设计模式 | 原型模式
21 0
|
3月前
|
设计模式 Go 开发工具
Golang设计模式——13原型模式
Golang设计模式——13原型模式
23 0
|
19天前
|
设计模式 SQL 算法
设计模式了解哪些,模版模式
设计模式了解哪些,模版模式
19 0
|
1月前
|
设计模式 Java uml
C++设计模式之 依赖注入模式探索
C++设计模式之 依赖注入模式探索
37 0
|
2月前
|
设计模式 前端开发 JavaScript
观察者模式 vs 发布-订阅模式:两种设计模式的对决!
欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚开始学习前端的读者们打造的。无论你是初学者还是有一些基础的开发者,我们都会在这里为你提供一个系统而又亲切的学习平台。我们以问答形式更新,为大家呈现精选的前端知识点和最佳实践。通过深入浅出的解释概念,并提供实际案例和练习,让你逐步建立起一个扎实的基础。无论是HTML、CSS、JavaScript还是最新的前端框架和工具,我们都将为你提供丰富的内容和实用技巧,帮助你更好地理解并运用前端开发中的各种技术。
|
15天前
|
设计模式 Java 数据库
小谈设计模式(2)—简单工厂模式
小谈设计模式(2)—简单工厂模式