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


Posted in Java/Android onJuly 21, 2021

引例

问题:

现在有一只羊(包含属性:名字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());
        //...
    }
}

运行结果

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

优缺点:

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

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

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

原型模式

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

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

UML类图:

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

  • 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());
        //...
    }
}

运行结果

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

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

现在小羊有了一个朋友小牛,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');
        //...
    }
}

运行结果

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

通过运行结果发现,浅拷贝通过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');
        //...
    }
}

运行结果

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

方法二:

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

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');
        //...
    }
}

运行结果

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

原型模式总结:

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

总结

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

Java/Android 相关文章推荐
ObjectMapper 如何忽略字段大小写
Jun 29 Java/Android
mybatis 解决从列名到属性名的自动映射失败问题
Jun 30 Java/Android
java解析XML详解
Jul 09 Java/Android
Java org.w3c.dom.Document 类方法引用报错
Aug 07 Java/Android
SpringDataJPA在Entity中常用的注解介绍
Dec 06 Java/Android
Netty客户端接入流程NioSocketChannel创建解析
Mar 25 Java/Android
Android Rxjava3 使用场景详解
Apr 07 Java/Android
零基础学java之带参数以及返回值的方法
Apr 10 Java/Android
多线程Spring通过@Scheduled实现定时任务
May 25 Java/Android
Spring Security动态权限的实现方法详解
Jun 16 Java/Android
springboot创建的web项目整合Quartz框架的项目实践
Jun 21 Java/Android
SpringBoot快速入门详解
java设计模式--三种工厂模式详解
gateway与spring-boot-starter-web冲突问题的解决
Jul 16 #Java/Android
springboot集成springCloud中gateway时启动报错的解决
Jul 16 #Java/Android
JavaWeb 入门篇(3)ServletContext 详解 具体应用
JavaWeb 入门:Hello Servlet
JavaWeb 入门篇:创建Web项目,Idea配置tomcat
You might like
解析PHP实现多进程并行执行脚本
2013/06/18 PHP
PHP遍历文件夹与文件类及处理类用法实例
2014/09/23 PHP
PHP 使用二进制保存用户状态的实例
2018/01/29 PHP
一起来写段JS drag拖动代码
2010/12/09 Javascript
关于onchange事件在IE和FF下的表现及解决方法
2014/03/08 Javascript
js中函数声明与函数表达式
2015/06/03 Javascript
jQuery模拟原生态App上拉刷新下拉加载更多页面及原理
2015/08/10 Javascript
jquery实现跳到底部,回到顶部效果的简单实例(类似锚)
2016/07/10 Javascript
利用jquery实现瀑布流3种案例
2016/09/18 Javascript
深入理解Vue transition源码分析
2017/07/30 Javascript
Vue.set()实现数据动态响应的方法
2018/02/07 Javascript
vue指令只能输入正数并且只能输入一个小数点的方法
2018/06/08 Javascript
代码整洁之道(重构)
2018/10/25 Javascript
node 版本切换的实现
2020/02/02 Javascript
JS实现简单打字测试
2020/06/24 Javascript
Vue登录拦截 登录后继续跳转指定页面的操作
2020/08/04 Javascript
Vue Router中应用中间件的方法
2020/08/06 Javascript
Node 使用express-http-proxy 做api网关的实现
2020/10/15 Javascript
ptyhon实现sitemap生成示例
2014/03/30 Python
Python Deque 模块使用详解
2014/07/04 Python
用Python编写一个简单的Lisp解释器的教程
2015/04/03 Python
Python正则匹配判断手机号是否合法的方法
2020/12/09 Python
Python简直是万能的,这5大主要用途你一定要知道!(推荐)
2019/04/03 Python
linux环境中没有网络怎么下载python
2019/07/07 Python
Django实现文章详情页面跳转代码实例
2020/09/16 Python
澳洲健康食品网上商店:Aussie Health Products
2018/06/15 全球购物
英国蛋糕装饰用品一站式商店:Craft Company
2019/03/18 全球购物
初入社会应届生求职信
2013/11/18 职场文书
手机银行营销方案
2014/03/14 职场文书
年会搞笑主持词串词
2014/03/24 职场文书
老人节标语大全
2014/10/08 职场文书
单位作风建设自查报告
2014/10/23 职场文书
三年级作文之趣事作文
2019/11/04 职场文书
十二月早安励志心语大全
2019/12/03 职场文书
python实现web邮箱扫描的示例(附源码)
2021/03/30 Python
Nginx域名转发使用场景代码实例
2021/03/31 Servers