Java设计模式之原型模式

需求

有一只羊叫tom, 年龄1岁, 颜色是白色, 请编写程序创建和tom属性完全相同的10只羊

传统方式解决克隆羊

直接上代码吧

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 + '\'' +
                '}';
    }
}
public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("tom", 1, "白色");
        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());

        System.out.println(sheep);
        System.out.println(sheep2);
        System.out.println(sheep3);
        System.out.println(sheep4);
        System.out.println(sheep5);
    }
}

输出结果:

Sheep{name='tom', age=1, color='白色'}
Sheep{name='tom', age=1, color='白色'}
Sheep{name='tom', age=1, color='白色'}
Sheep{name='tom', age=1, color='白色'}
Sheep{name='tom', age=1, color='白色'}

分析

  • 优点:
    比较好理解, 简单易操作
  • 缺点
  1. 在创建新的对象时, 总是需要重新获取原始对象的属性, 如果创建的对象比较复杂, 效率较低
  2. 总是需要重新初始化对象, 而不是动态的获得对象运行时的状态, 不够灵活

使用原型模式克隆羊

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

上代码:

public class Sheep implements Cloneable{

    private String name;
    private int age;
    private String color;
    private String address = "蒙古羊";


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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    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 + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    @Override
    protected Sheep clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sheep;
    }
}
public class Client {
    public static void main(String[] args) {

        System.out.println("原型模式完成对象的创建");
        Sheep sheep = new Sheep("tom", 1, "白色");
        Sheep sheep2 = sheep.clone();
        Sheep sheep3 = sheep.clone();
        Sheep sheep4 = sheep.clone();
        Sheep sheep5 = sheep.clone();

        System.out.println("sheep2 = " + sheep2);
        System.out.println("sheep3 = " + sheep3);
        System.out.println("sheep4 = " + sheep4);
        System.out.println("sheep5 = " + sheep5);

    }
}

浅拷贝与深拷贝

浅拷贝的介绍
  1. 对于数据类型是基本类型的成员变量, 浅拷贝会直接进行值传递, 将属性值复制一份给新的对象
  2. 对于数据类型是引用类型的成员变量, 浅拷贝会进行引用传递, 只是将该成员变量的引用值复制一份给新的对象, 在这种情况下在一个对象中修改该成员变量会影响到另一个对象的该成员变量
  3. 前面的克隆羊就是浅拷贝
    假如Sheep增加一个类型为Sheep的成员变量friendprivate Sheep friend, 这个成员变量在拷贝的时候就只会进行引用传递
深拷贝的介绍
  1. 复制对象的所有基本数据类型的成员变量
  2. 为所有引用数据类型的成员变量申请存储空间, 并复制每个引用数据类型成员变量所引用的对象, 直到该对象可达的所有对象, 也就是说, 要对整个对象进行拷贝
  3. 深拷贝实现方式1: 重写clone方法实现深拷贝
  4. 深拷贝实现方式2: 通过对象序列化实现深拷贝(推荐使用)

上代码:

方式一

首先, 这个DeepCloneableTarget是要拷贝的为引用数据类型的成员变量

public class DeepCloneableTarget implements Serializable, Cloneable {
    private static final long serialVersionUID = 1L;

    private String cloneName;

    private String cloneClass;


    public DeepCloneableTarget(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }

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

这个DeepProtoType是要拷贝的对象

public class DeepProtoType implements Serializable, Cloneable {

    private String name;
    private DeepCloneableTarget deepCloneableTarget;

    public String getName() {
        return name;
    }

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

    public DeepCloneableTarget getDeepCloneableTarget() {
        return deepCloneableTarget;
    }

    public void setDeepCloneableTarget(DeepCloneableTarget deepCloneableTarget) {
        this.deepCloneableTarget = deepCloneableTarget;
    }

    public DeepProtoType() {
    }

    public DeepProtoType(String name, DeepCloneableTarget deepCloneableTarget) {
        this.name = name;
        this.deepCloneableTarget = deepCloneableTarget;
    }

    // 深拷贝, 方式一: 使用clone方法
    @Override
    protected DeepProtoType clone() throws CloneNotSupportedException {
        DeepProtoType deepProtoType = null;
        deepProtoType = (DeepProtoType) super.clone();
        deepProtoType.setDeepCloneableTarget(deepProtoType.getDeepCloneableTarget().clone());
        return deepProtoType;
    }

}
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {


        DeepProtoType p = new DeepProtoType();
        p.setName("宋江");
        p.setDeepCloneableTarget(new DeepCloneableTarget("大牛", "小牛"));

        // 方式一完成深拷贝
        DeepProtoType p2 = p.clone();
        DeepProtoType p3 = p.clone();
        DeepProtoType p4 = p.clone();
        DeepProtoType p5 = p.clone();

        System.out.println("p.name = " + p.getName() + ", p.getDeepCloneableTarget().hashCode() = "+p.getDeepCloneableTarget().hashCode());
        System.out.println("p2.name = " + p2.getName() + ", p2.getDeepCloneableTarget().hashCode() = "+p2.getDeepCloneableTarget().hashCode());
        System.out.println("p3.name = " + p3.getName() + ", p3.getDeepCloneableTarget().hashCode() = "+p3.getDeepCloneableTarget().hashCode());
        System.out.println("p4.name = " + p4.getName() + ", p4.getDeepCloneableTarget().hashCode() = "+p4.getDeepCloneableTarget().hashCode());
        System.out.println("p5.name = " + p5.getName() + ", p5.getDeepCloneableTarget().hashCode() = "+p5.getDeepCloneableTarget().hashCode());

    }
}

输出结果:

p.name = 宋江, p.getDeepCloneableTarget().hashCode() = 1625635731
p2.name = 宋江, p2.getDeepCloneableTarget().hashCode() = 1580066828
p3.name = 宋江, p3.getDeepCloneableTarget().hashCode() = 491044090
p4.name = 宋江, p4.getDeepCloneableTarget().hashCode() = 644117698
p5.name = 宋江, p5.getDeepCloneableTarget().hashCode() = 1872034366
方式二
public class DeepProtoType implements Serializable, Cloneable {

    private String name;
    private DeepCloneableTarget deepCloneableTarget;

    public String getName() {
        return name;
    }

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

    public DeepCloneableTarget getDeepCloneableTarget() {
        return deepCloneableTarget;
    }

    public void setDeepCloneableTarget(DeepCloneableTarget deepCloneableTarget) {
        this.deepCloneableTarget = deepCloneableTarget;
    }

    public DeepProtoType() {
    }

    public DeepProtoType(String name, DeepCloneableTarget deepCloneableTarget) {
        this.name = name;
        this.deepCloneableTarget = deepCloneableTarget;
    }

    // 深拷贝, 方式二: 通过对象的序列化(推荐)
    public DeepProtoType 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);
            DeepProtoType copy = (DeepProtoType) ois.readObject();
            return copy;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {


        DeepProtoType p = new DeepProtoType();
        p.setName("宋江");
        p.setDeepCloneableTarget(new DeepCloneableTarget("大牛", "小牛"));

        // 方式二完成深拷贝
        DeepProtoType p2 = p.deepClone();
        DeepProtoType p3 = p.deepClone();
        DeepProtoType p4 = p.deepClone();
        DeepProtoType p5 = p.deepClone();


        System.out.println("p.name = " + p.getName() + ", p.getDeepCloneableTarget().hashCode() = "+p.getDeepCloneableTarget().hashCode());
        System.out.println("p2.name = " + p2.getName() + ", p2.getDeepCloneableTarget().hashCode() = "+p2.getDeepCloneableTarget().hashCode());
        System.out.println("p3.name = " + p3.getName() + ", p3.getDeepCloneableTarget().hashCode() = "+p3.getDeepCloneableTarget().hashCode());
        System.out.println("p4.name = " + p4.getName() + ", p4.getDeepCloneableTarget().hashCode() = "+p4.getDeepCloneableTarget().hashCode());
        System.out.println("p5.name = " + p5.getName() + ", p5.getDeepCloneableTarget().hashCode() = "+p5.getDeepCloneableTarget().hashCode());

    }
}

输出结果:

p.name = 宋江, p.getDeepCloneableTarget().hashCode() = 723074861
p2.name = 宋江, p2.getDeepCloneableTarget().hashCode() = 2093176254
p3.name = 宋江, p3.getDeepCloneableTarget().hashCode() = 1854731462
p4.name = 宋江, p4.getDeepCloneableTarget().hashCode() = 317574433
p5.name = 宋江, p5.getDeepCloneableTarget().hashCode() = 885284298

总结

  1. 创建新的对象比较复杂时, 可以使用原型模式简化对象的创建, 也能提高效率
  2. 不用重新初始化对象, 而是动态的获得对象运行时的状态
  3. 如果原始对象增加或减少属性, 其克隆对象也会发生相应的变化, 而无需修改代码
  4. 实现深克隆的时候可能需要比较复杂的代码
  5. 缺点: 需要为每一个类配备一个克隆方法, 对全新的类来说不难, 但是对已有类进行改造时, 需要修改其源代码, 违背了ocp原则, 这点需要注意

小结

代码地址:
https://github.com/mundane799699/DesignPattern/tree/master/Prototype/src/com/mundane/prototype
参考:

尚硅谷Java设计模式(图解+框架源码剖析)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容