java设计模式--原型模式详解

目录
  • 引例
  • 原型模式
  • 浅拷贝
    • 在原先Sheep类基础上实现Cloneable接口,重写clone方法。
    • 客户端调用
    • Sheep类
    • 新添的Cow类
    • 客户端调用克隆
  • 深拷贝
    • 1.Cow类也实现Cloneable接口
    • Sheep类的clone再添加调用cow的clone
    • 客户端调用
    • 1.Cow类实现序列化接口,不必实现Cloneable接口了
    • 2.在Sheep类实现序列化接口
    • 3.客户端调用
  • 总结

引例

问题:

现在有一只羊(包含属性:名字Dolly、年龄2),需要克隆10只属性完全相同的羊。

一般解法:

定义Sheep类表示羊,包括构造器、getter()和toString()。

public class Sheep {
    private String name;
    private int age;
    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在客户端实例化多利,然后再根据多利的属性去实例化10只羊。

public class Client {
    public static void main(String[] args) {
        Sheep sheepDolly=new Sheep("Dolly",2);
        Sheep sheep1 = new Sheep(sheepDolly.getName(), sheepDolly.getAge());
        Sheep sheep2 = new Sheep(sheepDolly.getName(), sheepDolly.getAge());
        Sheep sheep3 = new Sheep(sheepDolly.getName(), sheepDolly.getAge());
        //....
        System.out.println(sheep1+",hashCode:"+sheep1.hashCode());
        System.out.println(sheep2+",hashCode:"+sheep2.hashCode());
        System.out.println(sheep3+",hashCode:"+sheep3.hashCode());
        //...
    }
}

运行结果

优缺点:

这种方法是我们首先很容易就能想到的,也是绝大多数人的第一做法。

但缺点也很明显,每次创建新对象时需要获取原始对象的属性,对象复杂时效率很低;此外不能动态获得对象运行时的状态,若类增减属性需要改动代码。

下面我们看下原型模式的解法。

原型模式

原型模式(Prototype Pattern)是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。即用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。

工作原理:将原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。即用基类Object的clone()方法或序列化。

UML类图:

  • Prototype:原型类,声明一个克隆自己的接口
  • ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作
  • Client: 客户端让一个原型对象克隆自己,从而创建一个新的对象

原型模式又可分为浅拷贝和深拷贝,区别在于对引用数据类型的成员变量的拷贝,小朋友你是否有很多问号? 不急 ,看完这两种方法实现你就懂了。

浅拷贝

在原先Sheep类基础上实现Cloneable接口,重写clone方法。

public class Sheep implements Cloneable{
    private String name;
    private int age;
    @Override
    protected Object clone()  {//克隆该实例,使用默认的clone方法来完成
        Sheep sheep = null;
        try {
            sheep = (Sheep)super.clone();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return sheep;
    }
    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

客户端调用

public class Client {
    public static void main(String[] args) {
        Sheep sheepDolly=new Sheep("Dolly",2);
        Sheep sheep1 = (Sheep)sheepDolly.clone();
        Sheep sheep2 = (Sheep)sheepDolly.clone();
        Sheep sheep3 = (Sheep)sheepDolly.clone();
        //....
        System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());
        System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());
        System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());
        //...
    }
}

运行结果

至此,原型模式的浅拷贝也成功克隆了三个对象,但是看进度条发现并不简单。

现在小羊有了一个朋友小牛,Sheep类添加了一个引用属性Cow,我们同样再克隆一遍。

Sheep类

public class Sheep implements Cloneable{
    private String name;
    private int age;
    public Cow friend;//新朋友Cow对象,其余不变
    @Override
    protected Object clone()  {
        Sheep sheep = null;
        try {
            sheep = (Sheep)super.clone();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return sheep;
    }
    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

新添的Cow类

public class Cow {
    private String name;
    private int age;
    public Cow(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Cow{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

客户端调用克隆

public class Client {
    public static void main(String[] args) {
        Sheep sheepDolly=new Sheep("Dolly",2);
        sheepDolly.friend=new Cow("Tom",1); //并实例化朋友
        Sheep sheep1 = (Sheep)sheepDolly.clone();
        Sheep sheep2 = (Sheep)sheepDolly.clone();
        Sheep sheep3 = (Sheep)sheepDolly.clone();
        //....
        System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());
        System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');
        System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());
        System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');
        System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());
        System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');
        //...
    }
}

运行结果

通过运行结果发现,浅拷贝通过Object的clone()成功克隆实例化了三个新对象,但是并没有克隆实例化对象中的引用属性,也就是没有克隆friend对象(禁止套娃 ),三个新克隆对象的friend还是指向原克隆前的friend,即同一个对象。

这样的话,他们四个的friend是引用同一个,若一个对象修改了friend属性,势必会影响其他三个对象的该成员变量值。

小结:

  • 浅拷贝是使用默认的 clone()方法来实现
  • 基本数据类型的成员变量,浅拷贝会直接进行值传递(复制属性值给新对象)。
  • 引用数据类型的成员变量,浅拷贝会进行引用传递(复制引用值(内存地址)给新对象)。

深拷贝

方法一:

机灵的人儿看出,再clone一遍cow不就好了,但是手动递归下去不推荐。

1.Cow类也实现Cloneable接口

public class Cow implements Cloneable{
    private String name;
    private int age;
    public Cow(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //无引用类型,直接clone即可
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); //直接抛出了,没用try-catch
    }
    @Override
    public String toString() {
        return "Cow{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Sheep类的clone再添加调用cow的clone

public class Sheep implements Cloneable{
    private String name;
    private int age;
    public Cow friend;//新朋友Cow对象,其余不变
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        //完成对基本数据类型(属性)和String的克隆
        deep = super.clone();
        //对引用类型的属性,进行再次clone
        Sheep sheep = (Sheep)deep;
        sheep.friend  = (Cow)friend.clone();
        return sheep;
    }
    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

客户端调用

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheepDolly=new Sheep("Dolly",2);
        sheepDolly.friend=new Cow("Tom",1); //并实例化朋友
        Sheep sheep1 = (Sheep)sheepDolly.clone();
        Sheep sheep2 = (Sheep)sheepDolly.clone();
        Sheep sheep3 = (Sheep)sheepDolly.clone();
        //....
        System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());
        System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');
        System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());
        System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');
        System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());
        System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');
        //...
    }
}

运行结果

方法二:

通过对象序列化实现深拷贝(推荐)

1.Cow类实现序列化接口,不必实现Cloneable接口了

public class Cow implements Serializable {
    private String name;
    private int age;
    public Cow(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Cow{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2.在Sheep类实现序列化接口

public class Sheep implements Serializable { //实现序列化接口
    private String name;
    private int age;
    public Cow friend;

    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    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.printStackTrace();
            return null;
        } finally {
            //关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }
}

3.客户端调用

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheepDolly=new Sheep("Dolly",2);
        sheepDolly.friend=new Cow("Tom",1); //并实例化朋友
        Sheep sheep1 = (Sheep)sheepDolly.deepClone();
        Sheep sheep2 = (Sheep)sheepDolly.deepClone();
        Sheep sheep3 = (Sheep)sheepDolly.deepClone();
        //....
        System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());
        System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');
        System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());
        System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');
        System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());
        System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');
        //...
    }
}

运行结果

原型模式总结:

  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
  • 可以不用重新初始化对象,动态地获得对象运行时的状态。
  • 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
  • 若成员变量无引用类型,浅拷贝clone即可;若引用类型的成员变量很少,可考虑递归实现clone,否则推荐序列化。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 浅谈Java设计模式之原型模式知识总结

    如何使用? 1.首先定义一个User类,它必须实现了Cloneable接口,重写了clone()方法. public class User implements Cloneable { private String name; private int age; private Brother brother; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }

  • Java设计模式之Prototype原型模式

    一.场景描述 创建型模式中,从工厂方法模式,抽象工厂模式,到建造者模式,再到原型模式,我的理解是,创建对象的方式逐步从编码实现转向内存对象处理. 例如,在"仪器数据采集器"的子类/对象"PDF文件数据采集器"和"Excel文件数据采集器"的创建过程中, 工厂模式下定义各子类,并由(抽象)工厂类Factory创建,因此各子类可在类定义中定义各自的属性: 建造者模式下,通过不同的创建者类Builder创建不同的子对象,此时不再定义子类: 而原型模式下

  • 23种设计模式(3) java原型模式

    23种设计模式第三篇:java原型模式 定义: 通过复制现有的对象实例来创建新的对象实例. 实现: 实现Cloneable接口: Cloneable接口的作用是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法.在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常. 重写Object类中的clone方法: Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的

  • Java设计模式之原型模式详解

    一.前言 原型模式是一种比较简单的模式,也非常容易理解,实现一个接口,重写一个方法即完成了原型模式.在实际应用中,原型模式很少单独出现.经常与其他模式混用,他的原型类Prototype也常用抽象类来替代. 该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.在Java中,复制对象是通过clone()实现的,先创建一个原型类,通过实现Cloneable 接口 public class Prototype implements Cloneable { public

  • 设计模式之原型模式_动力节点Java学院整理

    定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 类型:创建类模式 类图: 原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype.Prototype类需要具备以下两个条件: 实现Cloneable接口.在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法.在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedExcepti

  • java设计模式--原型模式详解

    目录 引例 原型模式 浅拷贝 在原先Sheep类基础上实现Cloneable接口,重写clone方法. 客户端调用 Sheep类 新添的Cow类 客户端调用克隆 深拷贝 1.Cow类也实现Cloneable接口 Sheep类的clone再添加调用cow的clone 客户端调用 1.Cow类实现序列化接口,不必实现Cloneable接口了 2.在Sheep类实现序列化接口 3.客户端调用 总结 引例 问题: 现在有一只羊(包含属性:名字Dolly.年龄2),需要克隆10只属性完全相同的羊. 一般解

  • java设计模式-组合模式详解

    目录 组合模式 Demo 代码: 总结 组合模式 组合模式(Composite Pattern)又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象.组合模式依据树形结构来组合对象,用来表示部分以及整体层次.这种类型的设计模式属于结构型模式,它创建了对象组的树形结构. 主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦. 如何解决:树枝和叶子实现统一接口,树枝内部组合该接口. 何时使用:

  • java设计模式--桥接模式详解

    目录 引例 桥接模式 实战示例 代码: 总结 引例 需求:对不同手机类型的不同品牌(比如按键手机:诺基亚.翻盖手机:纽曼.智能手机:华为.小米)实现操作编程(比如: 开机.关机.打电话). 先来说说一般解法:将不同手机类型继承父类手机,最后各个品牌再继承对应手机类型: 弊端:乍一看没问题,但其实不易扩展(类爆炸),如果增加新的手机类型(比如新兴的折叠式),就需要增加各个手机品牌的类去继承(比如已继承智能手机的华为小米).同样如果我们增加一个手机品牌,也要在各个手机样式类下增加.违反了单一职责原则

  • java设计模式--策略模式详解

    目录 策略模式 Demo 代码: 总结 策略模式 策略模式(Strategy Pattern)属于行为型模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法.用算法族分别封装起来,实现同一个接口,让他们之间可以互相替换,让算法的变化独立于使用算法的客户. 主要解决:在有多种算法相似的情况下,使用 if-else 所带来的复杂和难以维护. 如何解决:将这些算法封装成一个一个的类,任意地替换. 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为. 使用场景: 如果在一个系统

  • java设计模式--建造者模式详解

    目录 引例 一般解法 代码: AbstractHouse(房子) Bungalow(平房) Villa (别墅) 客户端调用 建造者模式 建造者模式解法 代码: House类(Product产品) HouseBuilder类(Builder抽象建造者) 3.Bungalow类(ConcreteBuilder具体建造者A) 4.Villa类(ConcreteBuilder具体建造者B) 5.HouseDirector类(Director指挥者) 客户端调用 总结 引例 需求:建造房子,建造过程包括

  • Java之dao模式详解及代码示例

    什么是dao模式? DAO(Data Access Object)顾名思义是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作.在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中.用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法.在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储.DAO模式实

  • Java设计模式之装饰模式详解

    一.装饰模式引入例子 一个快餐店计算价格问题举例: 快餐店有炒面.炒饭这些快餐,可以额外附加鸡蛋.火腿.培根这些配菜,加配菜需要额外加钱,并且每个配菜的价钱不一样,计算快餐价格如何实现? 1.1 一般设计 1.2 使用继承方式的一般设计存在的问题 横向扩展性不好:如果要再加一种配料(火腿肠),我们就会发现需要给FriedRice和FriedNoodles分别定义一个子类.如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子,会出现类爆炸的问题. 继承适合于纵向扩展 二.装饰模式 2.1 装饰

  • Java设计模式之模板方法详解

    目录 概念 核心设计要点 优缺点 应用场景 模板方法和策略模式的区别 代码案例 概念 模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术,没有关联关系.因此,在模板方法模式的类结构图中,只有继承关系. 核心设计要点 AbstractClass:抽象类,定义并实现一个模板方法.这个模板方法定义了算法的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类去实现. ConcreteClass:实现实现父类所定义的一个或多个抽象方法.. 优缺点 优点 利用模板方法将相同处理逻

  • Java 设计模式之适配器模式详解

    目录 定义 结构图 使用场景 代码实现 Java代码实现 Python代码实现 定义 适配器将一个类的接口,转换成客户期望另一个接口.适配器让原本不兼容的类可以合作无间 结构图 如图所示,两脚插头如何能插入三脚插座,可以在中间加一个适配器进行转换,就能实现两脚插头能插入三脚插座. 使用场景 新的代码兼容旧的代码 使用别人好的代码到自己的代码中 代码实现 适配器模式有:对象适配器和类适配器 Java代码实现 java没有多继承,只能实现对象适配器 先创建两个接口 // 适配目标接口 public

随机推荐