zoukankan      html  css  js  c++  java
  • 设计模式之原型模式

    设计模式之原型模式

    1.克隆羊问题

    现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom羊属性完全相同的 5只羊。

    传统方式

    1.1 绘制UML类图

    image-20200920214301948

    1.2编写代码

    public class Sheep {
        private Integer age;
        private String name;
        private String color;
    
        public Sheep(Integer age, String name, String color) {
            this.age = age;
            this.name = name;
            this.color = color;
        }
    	//因为篇幅限制,这里省略getter和setter、有参构造方法以及toString的编写
    }
    public class Client {
        public static void main(String[] args) {
            Sheep sheep = new Sheep(1, "tom", "白色");
            Sheep sheep1 = new Sheep(sheep.getAge(), sheep.getName(), sheep.getColor());
            Sheep sheep2 = new Sheep(sheep.getAge(), sheep.getName(), sheep.getColor());
            Sheep sheep3 = new Sheep(sheep.getAge(), sheep.getName(), sheep.getColor());
            Sheep sheep4 = new Sheep(sheep.getAge(), sheep.getName(), sheep.getColor());
            System.out.println(sheep);
            System.out.println(sheep1);
            System.out.println(sheep2);
            System.out.println(sheep3);
            System.out.println(sheep4);
        }
    }
    

    image-20200920214703863

    1.3优缺点

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

    改进的思路分析:

    ​ 思路:Java 中 Object 类是所有类的根类,Object 类提供了一个 clone()方法,该方法可以将一个 Java 对象复制一份,但是需要实现

    clone 的 Java 类必须要实现一个接口 Cloneable,该接口表示该类能够复制且具有复制的能力 =>原型模式

    2.原型模式

    2.1 概述

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

    2.2 绘制UML类图

    image-20200921075102380

    UML类图说明:

    • Protptype:抽象原型类,实现Cloneable接口,也就是我们用户使用复制粘贴的功能。

    • ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作,说明一下它有被克隆复制的功能。

    • Client:客户类提出创建对象的请求。

    2.3 代码编写

    //prototype抽象原型类
    public abstract class Sheep implements Cloneable{
        private Integer age;
        private String name;
        private String color;
        private Sheep friend;//如果成员变量为对象,默认为浅拷贝
        public Sheep(Integer age, String name, String color) {
            this.age = age;
            this.name = name;
            this.color = color;
        }
        //重写clone方法
        @Override
        protected Sheep clone() throws CloneNotSupportedException {
            Sheep sheep =null;
    
            try {
                sheep = (Sheep) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return sheep;
        }
    //具体原型角色ConcretePrototype
     public class ConcretePrototype extends Sheep {
        public ConcretePrototype(Integer age, String name, String color) {
            super(age, name, color);
        }
    }
    //Client
    public class Client {
        public static void main(String[] args) throws CloneNotSupportedException {
            ConcretePrototype concretePrototype = new ConcretePrototype(20,"汤姆","蓝色");
            concretePrototype.setFriend(new Sheep(21,"朋友","白色"));
            ConcretePrototype clone = (ConcretePrototype) concretePrototype.clone();
            ConcretePrototype clone1 = (ConcretePrototype) concretePrototype.clone();
            ConcretePrototype clone2 = (ConcretePrototype) concretePrototype.clone();
            ConcretePrototype clone3 = (ConcretePrototype) concretePrototype.clone();
            System.out.println(concretePrototype+"@@@"+concretePrototype.hashCode()+"@@@"+concretePrototype.getFriend().hashCode());
            System.out.println(clone+"@@@"+clone.hashCode()+"@@@"+clone.getFriend().hashCode());
            System.out.println(clone1+"@@@"+clone1.hashCode()+"@@@"+clone1.getFriend().hashCode());
            System.out.println(clone2+"@@@"+clone2.hashCode()+"@@@"+clone2.getFriend().hashCode());
            System.out.println(clone3+"@@@"+clone3.hashCode()+"@@@"+clone3.getFriend().hashCode());
        }
    }
    

    image-20200921085436835

    我们可以看到,克隆出来的对象属性与之前的文件属性是一样的,但两个对象的HashCode是不一样的,也就是说创建了新的对象。

    但是ConcretePrototype内的friend对象拷贝的是引用。因为Object.clone()方法默认是浅拷贝。

    3.浅拷贝与深拷贝

    浅拷贝

    • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象

    • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是

      该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在

      一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

    • 我们上述案例就是浅拷贝,浅拷贝是使用默认的clone()方法来实现

    深拷贝

    • 复制对象的所有基本数据类型的成员变量值

    • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就

      是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝。

    实现方式:

    1. 重写clone方法来实现深拷贝

      			2. 通过对象序列化方式实现深拷贝(推荐)
      
    public class A implements Cloneable, Serializable {
        //必须显式重写
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    public class Sheep implements Cloneable,Serializable{
        private Integer age;
        private String name;
        private String color;
        private A a;//如果成员变量为对象,默认为浅拷贝
        public Sheep(Integer age, String name, String color) {
            this.age = age;
            this.name = name;
            this.color = color;
        }
        //方式一:重写clone方法
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Sheep sheep =null;
            try {
                //这里完成对基本数据类型和String的拷贝
                sheep = (Sheep) super.clone();
                //对引用类型的属性,进行单独处理
                A a1 = (A) a.clone();
                sheep.setA(a1);
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return sheep;
        }
        //方式二:通过对象的序列化方式实现(推荐)
        public Object deepClone(){
            //创建流对象
            ByteArrayOutputStream bos =null;
            ObjectOutputStream oos = null;
            ByteArrayInputStream bis = null;
            ObjectInputStream ois = null;
            Sheep obj = null;
            try {
                //序列化
                bos = new ByteArrayOutputStream();
                oos = new ObjectOutputStream(bos);
                //将当前这个对象以对象流的方式输出
                oos.writeObject(this);
                //反序列化
                bis = new ByteArrayInputStream(bos.toByteArray());
                ois = new ObjectInputStream(bis);
                obj = (Sheep)ois.readObject();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            finally {
                try {
                    if(bos!=null){
                        bos.close();
                    }
                    if(oos!=null){
                        oos.close();
                    }
                    if(bis!=null){
                        bis.close();
                    }
                    if(ois!=null){
                        ois.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return obj;
        }
    //省略getter、setter和toString
    }
    
    public class ConcretePrototype extends Sheep implements Cloneable , Serializable {
        public ConcretePrototype(Integer age, String name, String color) {
            super(age, name, color);
        }
    }
    
    public class Client {
        public static void main(String[] args) throws CloneNotSupportedException {
            //方式一:重写clone方法
            /*ConcretePrototype concretePrototype = new ConcretePrototype(20,"汤姆","蓝色");
            concretePrototype.setA(new A());
            ConcretePrototype clone = (ConcretePrototype) concretePrototype.clone();
            ConcretePrototype clone1 = (ConcretePrototype) concretePrototype.clone();
            ConcretePrototype clone2 = (ConcretePrototype) concretePrototype.clone();
            ConcretePrototype clone3 = (ConcretePrototype) concretePrototype.clone();
            System.out.println(concretePrototype+"@@@"+concretePrototype.hashCode());
            System.out.println(clone+"@@@"+clone.hashCode());
            System.out.println(clone1+"@@@"+clone1.hashCode());
            System.out.println(clone2+"@@@"+clone2.hashCode());
            System.out.println(clone3+"@@@"+clone3.hashCode());*/
            //方式二:序列化
            ConcretePrototype concretePrototype = new ConcretePrototype(20,"汤姆","蓝色");
            concretePrototype.setA(new A());
            ConcretePrototype clone = (ConcretePrototype) concretePrototype.deepClone();
            ConcretePrototype clone1 = (ConcretePrototype) concretePrototype.deepClone();
            ConcretePrototype clone2 = (ConcretePrototype) concretePrototype.deepClone();
            ConcretePrototype clone3 = (ConcretePrototype) concretePrototype.deepClone();
            System.out.println(concretePrototype+"@@@"+concretePrototype.hashCode());
            System.out.println(clone+"@@@"+clone.hashCode());
            System.out.println(clone1+"@@@"+clone1.hashCode());
            System.out.println(clone2+"@@@"+clone2.hashCode());
            System.out.println(clone3+"@@@"+clone3.hashCode());
        }
    }
    

    运行结果:

    方式一:

    image-20200921102451165

    方式二:

    image-20200921105301691

    注意:

    • A类,Sheep类,ConcretePrototype类都需要实现Cloneable和Serializable接口
    • 需要显式重写A类的clone方法,因为Object中的clone方法是protected修饰的,而protected修饰的方法只允许同包下的类和子类访问,Sheep类就调用不了A的clone方法,所以必须显示重写A类的方法。

    4.注意事项

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

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

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

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

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

  • 相关阅读:
    nginx+keepalived实现负载均衡nginx的高可用
    php7 安装swoole4.0.4
    Cannot find config.m4. Make sure that you run '/usr/local/php/bin/phpize' in the top level source directory of the module的 解决方法
    简析小黑是如何盗取cookie登录用户账号
    一个'&'引起md5签名不一致问题
    linux学习:curl与netcat用法整理
    swoole+websocket+redis实现一对一聊天
    使用COOKIE实现登录 VS 使用SESSION实现登录
    巧用PHP中__get()魔术方法
    用户表分表原理
  • 原文地址:https://www.cnblogs.com/gttttttt/p/13704428.html
Copyright © 2011-2022 走看看