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原则,这点需要特别注意.

相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
相关文章
|
6天前
|
设计模式 安全 Java
【设计模式】原型模式
【设计模式】原型模式
|
6天前
|
设计模式 Java
【设计模式系列笔记】原型模式
原型模式(Prototype Pattern)是一种创建型设计模式,其主要目的是通过复制现有对象来创建新对象,而无需知道其具体类型。这种模式属于对象创建型模式,通过克隆来避免使用new关键字创建对象,提高性能和降低系统的耦合度。
33 6
|
6天前
|
设计模式 Java
小谈设计模式(10)—原型模式
小谈设计模式(10)—原型模式
|
6天前
|
设计模式 Java
23种设计模式,原型模式的概念优缺点以及JAVA代码举例
【4月更文挑战第10天】原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新的对象,而无需知道如何创建的细节。这种模式的核心思想是基于一个原型实例,通过复制这个原型来创建新的对象
27 7
|
6天前
|
设计模式 Java
设计模式之原型模式
设计模式之原型模式
|
6天前
|
设计模式 存储 JSON
Java设计模式-原型模式
原型模式也是创建对象的一种方式,它一般用在这样的场景:系统中存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂而且耗费资源。这个时候使用原型模式的克隆方式,能够节省不少时间。比如Java 类中提供的`Object clone()`就是原型模式的应用。
31 1
Java设计模式-原型模式
|
6天前
|
设计模式 API
【设计模式】适配器和桥接器模式有什么区别
【设计模式】适配器和桥接器模式有什么区别
10 1
|
6天前
|
设计模式
【设计模式】张一鸣笔记:责任链接模式怎么用?
【设计模式】张一鸣笔记:责任链接模式怎么用?
11 1
|
6天前
|
设计模式 uml
【设计模式】建造者模式就是游戏模式吗?
【设计模式】建造者模式就是游戏模式吗?
11 0
|
6天前
|
设计模式 Java uml
【设计模式】什么是工厂方法模式?
【设计模式】什么是工厂方法模式?
8 1