05.原型模式详解

原型模式的定义

        是指原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象,原型模式是复制对象的值,而不是对象本身。调用者不需要知道创建对象的细节,并且不调用构造函数,直接走字节码拷贝,属于创建型模式

适用场景

  • 类初始化消耗资源较多
  • new产生的一个对象需要非常繁琐的过程(包括数据准备、访问权限等)
  • 构造函数较为复杂时
  • 循环中需要生成大量对象时,导致可读性下降

典型的应用产品

  • BeanUtils.copy()
  • JSON.parseObject()
  • Guava工具类库

原型模式最大的好处:简化创建对象的繁琐过程,且不需要通过构造函数创建对象

浅克隆

/**
 * 原型接口,提供clone方法
 */
public interface Prototype {
    /**
     * 返回一个clone后的原型对象
     *
     * @return
     */
    Prototype clone();
}
/**
 * 原对象
 * @author wcj
 * @description
 * @date 2019/8/22 10:51
 */
public class UserDto implements Prototype{
    private int age;
    private String name;
    private String mobile;
    //getter、setter方法省略
    @Override
    public Prototype clone() {
        UserDto dto = new UserDto();
        dto.setAge(this.age);
        dto.setName(this.name);
        dto.setMobile(this.mobile);
        return dto;
    }
}
/**
 * 用于克隆原对象
 *
 * @author wcj
 * @description
 * @date 2019/8/22 10:53
 */
public class UserVo {
    public Prototype startClone(Prototype prototype) {
        return prototype.clone();
    }
}
/**
 * @author wcj
 * @description 浅克隆测试类
 * @date 2019/8/22 11:05
 */
public class ShallowCloneTest {
    public static void main(String[] args) {
        //原对象
        UserDto userDto = new UserDto();
        userDto.setAge(18);
        userDto.setName("Jack");
        userDto.setMobile("18010913348");
        //克隆对象
        UserVo vo = new UserVo();
        UserDto prototype = (UserDto) vo.startClone(userDto);
        System.out.println("原对象引用类型地址的值:" + userDto.getMobile());
        System.out.println("克隆后对象引用类型地址的值:" + prototype.getMobile());
        System.out.println("对象引用类型地址是否一致:" + (userDto.getMobile() == prototype.getMobile()));
        System.out.println("对象是否一致:" + (userDto == prototype));
    }
}
输出结果为:
true,false
说明克隆对象和原对象虽然是两个新对象,但是引用类型的地址是一致的,说明拷贝的不是引用的值,拷贝的是引用地址

是克隆的引用类型的地址,而不是引用类型的值,如果当原对象的值发生了改变,那么克隆对象也会随之发生改变,这两个对象就不是两个独立的对象了

深克隆

        举个显而易见的例子,漩涡鸣人是一个忍者,鸣人有自己的忍具苦无,而忍者又是一个抽象的概念,有自己的属性,如果浅克隆,那么就相当于分身术后的鸣人和鸣人自己公用一个忍者,这肯定不合理,也无法战斗了,那么这种情况下,只有分身和主体都有各自的忍具才合理,这就可以理解是深克隆。代码如下

/**
 * 忍者
 * Created by wcj.
 */
public class Ninja {
    public int height;
    public int weight;
    public Date birthday;
}
/**
 * 忍具:苦无
 * Created by wcj.
 */
public class Kunai implements Serializable {
    public float h = 100;
    public float d = 10;
    //放大
    public void big() {
        this.d *= 2;
        this.h *= 2;
    }
    //缩小
    public void small() {
        this.d /= 2;
        this.h /= 2;
    }
}
/**
 * 漩涡鸣人
 * Created by wcj.
 */
public class UzumakiNaruto extends Ninja implements Cloneable, Serializable {
    public Kunai kunai;
    public UzumakiNaruto() {
        this.birthday = new Date();
        this.kunai = new Kunai();
    }
    /**
     * 这里采用深克隆,可能会出现单例被破坏的情况,所以原型模式和单例模式有点互斥的
     *
     * @return
     */
    @Override
    protected Object clone() {
        return this.deepClone();
    }
    /**
     * 字节码拷贝
     */
    public Object deepClone() {
        try {
            //内存中完成对象的读写操作,而且是通过字节码直接操作的,与序列化操作类似
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            //完整的新的对象,由readObject源码可知,是通过构造器new出来的
            UzumakiNaruto copy = (UzumakiNaruto) ois.readObject();
            copy.birthday = new Date();
            return copy;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 浅克隆
     */
    public UzumakiNaruto shallowClone(UzumakiNaruto target) {
        UzumakiNaruto uzumakiNaruto = new UzumakiNaruto();
        uzumakiNaruto.height = target.height;
        uzumakiNaruto.weight = target.height;
        uzumakiNaruto.kunai = target.kunai;
        uzumakiNaruto.birthday = new Date();
        return uzumakiNaruto;
    }
}
/**
 * 克隆测试类
 */
public class DeepCloneTest {
    public static void main(String[] args) {
        UzumakiNaruto uzumakiNaruto = new UzumakiNaruto();
        try {
            UzumakiNaruto clone = (UzumakiNaruto) uzumakiNaruto.clone();
            System.out.println("测试深克隆:" + (uzumakiNaruto.kunai == clone.kunai));
        } catch (Exception e) {
            e.printStackTrace();
        }
        UzumakiNaruto q = new UzumakiNaruto();
        UzumakiNaruto n = q.shallowClone(q);
        System.out.println("测试浅克隆:" + (q.kunai == n.kunai));
    }
}

Cloneable和Serializable都是空接口,没有定义任何方法

ArrayList中有个copy(),是深克隆还是浅克隆?

由源码可以看出,ArrayList类是实现了Cloneable的接口,并且调用Arrays的copyOf()

由Arrays类的copyOf()可看出,是创建新对象,将值拷贝到新对象中,调用的是底层的System.arraycopy()

原型模式就是如果快速构建对象方法的总结。有两种方法

  • 简单工厂将getter、setter封装到某个方法中(浅克隆)
  • 实现JDK提供的Cloneable接口,重写Object类的clone方法(使用字节码复制的方法),实现快速复制(深克隆)

比如,spring框架中的配置:scope="prototype"

原型模式的缺点

  • 必须配备克隆或可拷贝方法
  • 对克隆复杂对象或对克隆出的对象进行复杂改造时,易带来风险
  • 深拷贝、浅拷贝要运用得当
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芦蒿炒香干

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值