【Java设计模式003】原型模式

概述

大家好,个人gzh是大猪和小猪的小家 ,我们的gzh是朝阳三只大明白,满满全是干货,分享近期的学习知识以及个人总结(包括读研和IT),跪求一波关注,希望和大家一起努力、进步!!

原型模式解决的主要问题是如何快速的复制一个已经存在的对象,一个普遍的做法是构建一个属于相同类的对象,然后遍历原始对象的所有属性值并复制到新对象中。这样的做法有一些问题,不是每一个对象都可以通过这种方式进行复制,且这么做的编程代价过高,比方说:

java 复制代码
class Main{
	public static void main(String[] args) {
        Sheep sheep = new Sheep("tom", 1, "red");
        new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
    }
}

这样做比较好理解,简单易于操作,但是复制对象的效率很低(现在只有三个参数需要处理)。而原型模式就可以解决这个问题,原型模式是用于创建重复的对象,同时又能保证性能的一种模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式的角色如下:

  • 抽象原型类:规定了具体原型对象必须实现的 clone() 方法,一般也只有这么一个方法。
  • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
  • 访问类:使用具体原型类中的 clone() 方法来复制新的对象。

原型模式的优点在于:

  1. 你可以克隆对象,而无需与它们所属的具体类相耦合;
  2. 你可以克隆预生成原型,避免反复运行初始化代码;
  3. 你可以更方便地生成复杂对象;
  4. 你可以用继承以外的方式来处理复杂对象的不同配置;

原型模式的缺点在于:

  1. 克隆包含循环引用的复杂对象可能会非常麻烦。

实现

原型模式的克隆分为浅克隆和深克隆:

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

Java 中的 Object 类提供了 clone() 方法,该方法实现了浅克隆,在 Java 中 Cloneable 接口就是原型模式中的抽象原型类 ,而任何实现了 Cloneable 接口的类就是具体原型类,代码如下:

java 复制代码
public class Realizetype implements Cloneable {
    public Realizetype() {
        System.out.println("具体原型创建完成");
    }

    @Override
    protected Realizetype clone() throws CloneNotSupportedException {
        System.out.println("具体原型复制成功");
        return (Realizetype) super.clone();
    }
}

测试类:

java 复制代码
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Realizetype prototye = new Realizetype();
        Realizetype clonedObject = prototye.clone();
		// false
        System.out.println(prototye == clonedObject);
    }
}

上面的拷贝方式是浅拷贝,如果需要实现深拷贝,可以考虑使用序列化、反序列化的方式进行实现,示例代码如下:

java 复制代码
public class Sheep implements Cloneable, Serializable {
	...
    // 深克隆方法
    public Sheep deepClone() throws IOException {
        //创建对象流对象
        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("a.txt")));
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("a.txt")));

        try {
            //序列化
            oos.writeObject(this);

            //反序列化
            Sheep newSheep = (Sheep) ois.readObject();

            return newSheep;
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        } finally {
            oos.close();
            ois.close();
        }
    }
}

小结一下,原型模式的实现思想如下:

  1. 创建抽象原型类,声明克隆方法;
  2. 创建具体原型类 ,实现抽象原型类,重写克隆方法;
  3. 客户端使用原型对象;

应用

  1. 如果你需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式;
  2. 如果子类的区别仅在于其对象的初始化方式,那么你可以使用该模式来减少子类的数量,这么做的话客户端不必根据子类进行实例化,只需要找到合适的原型然后进行克隆即可;
  3. 当一个对象的构建代价过高时。例如某个对象里面的数据需要访问数据库才能拿到,而我们却要多次构建这样的对象;
  4. 当构建的多个对象,均需要处于某种原始状态时,就可以先构建一个拥有此状态的原型对象,其他对象基于原型对象来修改。

个人认为原型模式的一个重要应用在于减少保护性拷贝 的代码量,保护性拷贝是指为了防止客户端对类的约束条件产生破坏,传递对象的时候要进行拷贝,一个简单的示例如下:

java 复制代码
public class Period {
    private final Date start;
    private final Date end;

    public Period(Date start, Date end) {
        this.start = new Date(start.getTime());
        this.end = new Date(end.getTime());

        if (this.start.compareTo(end) > 0){
            throw new IllegalArgumentException(this.start + "after" + this.end);
        }
    }

    public Date getStart() {
        // 直接return会破坏类
        return new Date(start.getTime());
    }

    public Date getEnd() {
        // 直接return会破坏类
        return new Date(end.getTime());
    }
}

类的约束条件是开始时间要小于结束时间,且这两个成员变量是私有的,但是如果 getter 方法中直接返回原始对象,那么就会破坏原本的约束,也就是说如果一个类从客户端得到或者返回一个可变组件,那么就必须进行保护性拷贝。如果使用了原型模式,直接返回要进行保护对象的 clone() 方法的返回值即可,这样大大减少了代码的书写量。

除此之外还可以创建一个中心化原型注册表,用于存储常用原型。可以新建一个工厂类来实现注册表,或者添加一个静态方法,不管是哪一种方式,这些方法必须能够根据客户端代码设定的条件进行搜索。搜索条件可以是简单的字符串,或者是一组复杂的搜索参数。找到合适的原型后,注册表应对原型进行克隆,并将复制生成的对象返回给客户端。

往期回顾

  1. 【Java设计模式002】工厂模式
  2. 【Java设计模式001】单例模式

文中难免会出现一些描述不当之处(尽管我已反复检查多次),欢迎在留言区指正,相关的知识点也可进行分享,希望大家都能有所收获!!如果觉得我的文章写得还行,不妨支持一下。你的每一个转发、关注、点赞、评论都是对我最大的支持!

相关推荐
TDengine (老段)3 小时前
TDengine 使用最佳实践(2)
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
Deng9452013144 小时前
基于大数据的电力系统故障诊断技术研究
大数据·matplotlib·深度特征提取·随机森林分类算法·标签编码
FreeBuf_6 小时前
黄金旋律IAB组织利用暴露的ASP.NET机器密钥实施未授权访问
网络·后端·asp.net
小菜鸡06267 小时前
FlinkSQL通解
大数据·flink
张小洛7 小时前
Spring AOP 是如何生效的(入口源码级解析)?
java·后端·spring
寅鸷7 小时前
es里为什么node和shard不是一对一的关系
大数据·elasticsearch
DKPT7 小时前
Java设计模式之行为型模式(观察者模式)介绍与说明
java·笔记·学习·观察者模式·设计模式
why技术8 小时前
也是出息了,业务代码里面也用上算法了。
java·后端·算法
络78 小时前
Java4种设计模式详解(单例模式、工厂模式、适配器模式、代理模式)
单例模式·设计模式·代理模式·适配器模式·工厂模式
贱贱的剑8 小时前
5.适配器模式
设计模式·适配器模式