• 设计模式之创建型模式:原型模式


    前言

    传统方法要创建一个新对象B与另一个对象A的属性值完全一致,就是重新获取A的属性值,然后通过new进行实例化,这种创建对象的方法复杂且效率低,而且需要重新初始化对象,不能动态的获取A的运行时状态数据

    针对传统方法的问题,提出了原型模式,原型模式是将A对象(原型对象),通过克隆的方式实现对其拷贝,然后产生一个新对象B,这个B对象与A对象的属性值完全一致,原型模式拷贝对象,效率高,操作简单,能捕捉原型对象的运行时状态数据

    原型模式就像克隆羊多莉一样,通过一只原型羊克隆出另一个完全一样的羊,本文以克隆羊对象为案例,提出传统解决方案及原型模式的解决方案,并对原型模式中的浅拷贝和深拷贝进行剖析。

    原型模式就是拷贝对象。

    一、克隆羊案例传统解决方案

    传统方式实现Sheep原型对象的复制示意图如下:

    1. 获取sheep对象(原型对象)的属性值
    2. 通过new实例化新的对象
      在这里插入图片描述
      示例代码如下:
    //原型类
    public class Sheep {
    	private String name;
    	private int age;
    	private String color;
    	public Sheep(String name, int age, String color) {
    		super();
    		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) {
    		// TODO Auto-generated method stub
    		//原型对象
    		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());
    		//....
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    传统方法编写复杂且效率低。

    二、克隆羊案例原型模式解决方案

    原型模式只需要实现Cloneable接口,然后重写Object类中的clone方法,即可通过clone方法实现原型对象的拷贝。
    在这里插入图片描述
    示例代码如下:

    //实现Cloneable接口
    public class Sheep implements Cloneable {
    	private String name;
    	private int age;
    	private String color;
    	public Sheep(String name, int age, String color) {
    		super();
    		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;
    	}
    
        //实现clone方法
    	@Override
    	protected Object clone() throws CloneNotSupportedException {
    		return super.clone();
    	}
    
    	@Override
    	public String toString() {
    		return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
    	}
    }
    
    public class Client {
    
    	public static void main(String[] args) {
    		// 原型对象
    		Sheep sheep = new Sheep("tom", 1, "白色");
    		
    		//克隆对象
    		Sheep sheep2 = (Sheep)sheep.clone(); //克隆
    		Sheep sheep3 = (Sheep)sheep.clone(); //克隆
    		Sheep sheep4 = (Sheep)sheep.clone(); //克隆
    		Sheep sheep5 = (Sheep)sheep.clone(); //克隆
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    三、原型模式浅拷贝与深拷贝

    上面的案例,虽然重写Object中的clone方法,但仅仅采用父类的实现,这种方式只是实现了浅拷贝,即如果拷贝的原型对象中存在引用类型,那么浅拷贝是只拷贝了引用类型的引用地址,并未真正拷贝一份空间。

    1. 什么是浅拷贝,深拷贝

    具体一点的例子,就比如你觉得邻居家的房子盖的很漂亮,你也想拥有像邻居家一样的房子,如果你采用浅拷贝的方式,那当你向外人介绍的时候,只能指着邻居家房子说:“你瞧那个房子,就是我的房子”,也就是你和邻居家共同拥有一个同一个房子,如果你邻居要装修为其他样式,那你也跟着变。但如果你采用深拷贝的方式,那就是你按照邻居的房子样式,自己盖一个和邻居家一模一样的房子,当你再向外人介绍的时候,你就会指着自己的房子说:“你瞧这个房子,就是我的房子”,如果你邻居要把他的房子装修为其他样式,那丝毫影响不了你的房子。

    2. 浅拷贝如何实现

    浅拷贝的只需要将原型类实现Cloneable 接口,然后将重写的clone方法直接采用父类的实现即可。

    3. 改写clone方法实现深拷贝

    如A类中有一个B类引用,如果要通过改写clone方法实现对A类进行深拷贝,代码示例如下:

    public class A implements Cloneable{
        public String Info;
        public B b;
    
        public A(String info, B b) {
            Info = info;
            this.b = b;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            A clone = (A)super.clone();
            clone.b= (B) b.clone();//对引用类进行深拷贝
            return clone;
        }
    }
    
    public class B implements Cloneable{
        public String Info;
    
        public B(String info) {
            Info = info;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    
    public class test {
        public static void main(String[] args) throws CloneNotSupportedException {
            A a = new A("原型类", new B("原型类中的引用类"));
            A a1 = (A) a.clone();
    
            System.out.println(a.b.Info);
            System.out.println(a1.b.Info);
    
            System.out.println(a.b.hashCode());
            System.out.println(a1.b.hashCode());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    输出结果:
    在这里插入图片描述

    4. 通过序列化实现深拷贝【推荐】

    与上一个案例一样,若A类中有一个B类引用,如果要通过序列化实现对A类进行深拷贝,代码示例如下:

    //A类实现序列化
    public class A implements Serializable{
        public String Info;
        public B b;
    
        public A(String info, B b) {
            Info = info;
            this.b = b;
        }
        
        //通过序列化实现深拷贝
        public Object deepClone() {
            A deepCopy=null;
            ByteArrayOutputStream bos=null;
            ObjectOutputStream oos=null;
    
            ObjectInputStream ois=null;
            try {
                //序列化
                bos = new ByteArrayOutputStream();
                oos = new ObjectOutputStream(bos);
                oos.writeObject(this);
                //反序列化
                ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
                deepCopy = (A) ois.readObject();
            }
            catch (Exception ex)
            {
                System.out.println(ex.getMessage());
            }
            finally {
                //关闭流
                try {
                    bos.close();
                    oos.close();
                    ois.close();
                } catch (IOException e) {
                    System.out.println(e.getMessage());
                }
            }
            return deepCopy;
        }
    }
    
    public class B implements Serializable{
        public String Info;
    
        public B(String info) {
            Info = info;
        }
    }
    
    public class test {
        public static void main(String[] args) throws CloneNotSupportedException {
            A a = new A("原型类", new B("原型类中的引用类"));
            A a1 = (A) a.deepClone();
    
            System.out.println(a.b.Info);
            System.out.println(a1.b.Info);
    
            System.out.println(a.b.hashCode());
            System.out.println(a1.b.hashCode());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    在这里插入图片描述

  • 相关阅读:
    Word文件损坏怎么办?这3个方法教你轻松解决!
    【自然语言处理(NLP)】基于LSTM的谣言检测
    [附源码]Python计算机毕业设计Django人员信息管理
    Nginx 高性能架构解析
    数据结构 ----- 插值查找
    用C语言解决三个整数比大小,x,y,z三个整数求最小整数,从键盘上输入3个不同的整数×,y,Z,请设计一个算法找出其中最小的数,并画出流程图。
    Linux (Ubuntu)c编程 (入门必看)
    部署kafka后启动报错(坑):无法指定被请求的地址
    Redis从理论到实战:使用Redis实现商铺查询缓存(逐步分析缓存更新策略)
    2:开发环境搭建-Java Web
  • 原文地址:https://blog.csdn.net/qq_34720818/article/details/126506729