设计模式-第五课-原型模式

        一句话概括“原型模式最主要的其实是深浅拷贝问题”

        原型模式(Prototype Pattern),还是创建性的设计模式,主要还是遇到复杂的对象创建时候可以通过原型模式来减少操作,增加效率。

        举例常规的创建对象:

@Data
public class AcAccountExt implements Serializable {
    private Integer aeId;

    private Integer aeAcctType;

    private Integer aeAcctId;

    private Integer aeUserId;

    private String aeAcctName;

    private String aeAcctCard;

    private String aeAcctBank;

    private String aeAcctCode;

    private String aeAcctRelCode;

    private String aeBankProv;

    private String aeBankCity;

    private String aeIdCard;

    private String aeCrtTime;

    private Integer aeState;

    private String aeStateTime;

    private String aeAppId;

    private String aePrivateKey;

    private String aePublicKey;

    private String aeOtherPublicKey;

    private String aeAlipayUserId;

    private Integer aePubIdtyId;

    private Integer aeQuerytransFlag;

    private Integer aeQuerytransChannel;

    private static final long serialVersionUID = 1L;


    public static void main(String[] args) {
        AcAccountExt ae = new AcAccountExt();
        ae.setAeAcctBank("");
        ae.setAeAcctId(123);
        ... 省略其他的
    }
}

        最初时候,需要new完对象后一个个赋值,上面的例子是创建一个,如果创建多个,会非常费力,尤其是内容相差不多的情况下,可能只是其中的一两个字段修改,其他都一样。

        此时就应运而生了原型模式(不太准确,应该是顺势被总结成了方法论),这样就可以这么写了:

@Data
public class AcAccountExt implements Serializable,Cloneable {
    private Integer aeId;

    private Integer aeAcctType;

    private String aeAcctBank;

    private Integer aeAcctId;
    
    //省略一堆无用的字段

    private static final long serialVersionUID = 1L;

    // 这个重写写不写都行,或者只要简单的 return (AcAccountExt)super.clone();就好,这样可以有更多自己的定制化信息或者异常处理
    @Override
    protected AcAccountExt clone() throws CloneNotSupportedException {
        AcAccountExt ae = null;
        try {
            ae = (AcAccountExt) super.clone();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return ae;
    }

    public static void main(String[] args) {
        AcAccountExt ae = new AcAccountExt();
        ae.setAeAcctBank("11");
        ae.setAeAcctId(123);
        AcAccountExt ae2 = null;
        try {
            ae2 = ae.clone();
            ae2.setAeAcctId(222);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        System.out.println(ae);
        System.out.println(ae2);
    }
}

运行结果如下:

AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=123, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null)

AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=222, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null)

注意下几点:

  1. 实现的接口要加上Cloneable
  2. 如果有特殊需求可以重写一下clone方法(深浅拷贝在此区分

        上面就可以把正常的原型模式说差不多,但是会有一些问题,如果这个类稍微复杂点,不只是普通类型的,而替换成别的类呢?

还是这个类,举个例子:


@Data
public class AcAccountExt implements Cloneable {
    private Integer aeId;

    private Integer aeAcctId;

    private String aeAcctBank;

    // 去掉没用的

    private AcAccount aa;

    public static void main(String[] args) {
        AcAccountExt ae = new AcAccountExt();
        ae.setAeAcctBank("11");
        ae.setAeAcctId(123);
        ae.aa = new AcAccount("123",111);

        AcAccountExt ae2 = null;
        try {
            ae2 = (AcAccountExt) ae.clone();
            ae2.setAeAcctId(222);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        System.out.println(ae);
        System.out.println(ae2);

        System.out.println("修改一下");
        ae.aa.setAcCrtTime("222");
        System.out.println(ae);
        System.out.println(ae2);

    }
}

执行结果是:

AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=123, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null, aa=AcAccount [Hash = 305768267, acId=111, acUserId=null, acCustId=null, acAcctType=null, acChannel=null, acChnlGrp=null, acInitVal=null, acAvbBlance=null, acFrezFee=null, acCrtTime=123, acCanCharge=null, acCanWithdraw=null, acCanPay=null, acCanAdjst=null, acCanInvc=null, acState=null, acStateTime=null, acRelationUserId=null, acPubIdtyId=null])
AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=222, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null, aa=AcAccount [Hash = 305768267, acId=111, acUserId=null, acCustId=null, acAcctType=null, acChannel=null, acChnlGrp=null, acInitVal=null, acAvbBlance=null, acFrezFee=null, acCrtTime=123, acCanCharge=null, acCanWithdraw=null, acCanPay=null, acCanAdjst=null, acCanInvc=null, acState=null, acStateTime=null, acRelationUserId=null, acPubIdtyId=null])
修改一下
AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=123, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null, aa=AcAccount [Hash = -849224565, acId=111, acUserId=null, acCustId=null, acAcctType=null, acChannel=null, acChnlGrp=null, acInitVal=null, acAvbBlance=null, acFrezFee=null, acCrtTime=222, acCanCharge=null, acCanWithdraw=null, acCanPay=null, acCanAdjst=null, acCanInvc=null, acState=null, acStateTime=null, acRelationUserId=null, acPubIdtyId=null])
AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=222, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null, aa=AcAccount [Hash = -849224565, acId=111, acUserId=null, acCustId=null, acAcctType=null, acChannel=null, acChnlGrp=null, acInitVal=null, acAvbBlance=null, acFrezFee=null, acCrtTime=222, acCanCharge=null, acCanWithdraw=null, acCanPay=null, acCanAdjst=null, acCanInvc=null, acState=null, acStateTime=null, acRelationUserId=null, acPubIdtyId=null])

        内容有点多,可以看到修改了ae的aa,但是ae2的也改了,还有可以看到修改前的一组的哈希值是相同的,修改后的也是相同的,说明其实只是指向了同一个地址,一个修改都修改了。

        具体修改的方式可以比较容易想到一种,就是前面提到一嘴的重写clone方法,每个值都拿出来赋值一下,如果是新的类就新建一个赋值。

        除了重写clone方法,还有一种是通过序列化来实现。


    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);
            AcAccountExt copyObj = (AcAccountExt)ois.readObject();
            return copyObj;
        } 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());
            }
        }
    }

    public static void main(String[] args) {
        AcAccountExt ae = new AcAccountExt();
        ae.setAeAcctBank("11");
        ae.setAeAcctId(123);
        ae.aa = new AcAccount("123",111);

        AcAccountExt ae2 = null;
        ae2 = (AcAccountExt) ae.deepClone();
        ae2.setAeAcctId(222);

        System.out.println(ae);
        System.out.println(ae2);

        System.out.println("修改一下");
        ae.aa.setAcCrtTime("222");
        System.out.println(ae);
        System.out.println(ae2);

    }

输出结果:

AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=123, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null, aa=AcAccount [Hash = 305768267, acId=111, acUserId=null, acCustId=null, acAcctType=null, acChannel=null, acChnlGrp=null, acInitVal=null, acAvbBlance=null, acFrezFee=null, acCrtTime=123, acCanCharge=null, acCanWithdraw=null, acCanPay=null, acCanAdjst=null, acCanInvc=null, acState=null, acStateTime=null, acRelationUserId=null, acPubIdtyId=null])
AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=222, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null, aa=AcAccount [Hash = 305768267, acId=111, acUserId=null, acCustId=null, acAcctType=null, acChannel=null, acChnlGrp=null, acInitVal=null, acAvbBlance=null, acFrezFee=null, acCrtTime=123, acCanCharge=null, acCanWithdraw=null, acCanPay=null, acCanAdjst=null, acCanInvc=null, acState=null, acStateTime=null, acRelationUserId=null, acPubIdtyId=null])
修改一下
AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=123, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null, aa=AcAccount [Hash = -849224565, acId=111, acUserId=null, acCustId=null, acAcctType=null, acChannel=null, acChnlGrp=null, acInitVal=null, acAvbBlance=null, acFrezFee=null, acCrtTime=222, acCanCharge=null, acCanWithdraw=null, acCanPay=null, acCanAdjst=null, acCanInvc=null, acState=null, acStateTime=null, acRelationUserId=null, acPubIdtyId=null])
AcAccountExt(aeId=null, aeAcctType=null, aeAcctId=222, aeUserId=null, aeAcctName=null, aeAcctCard=null, aeAcctBank=11, aeAcctCode=null, aeAcctRelCode=null, aeBankProv=null, aeBankCity=null, aeIdCard=null, aeCrtTime=null, aeState=null, aeStateTime=null, aeAppId=null, aePrivateKey=null, aePublicKey=null, aeOtherPublicKey=null, aeAlipayUserId=null, aePubIdtyId=null, aeQuerytransFlag=null, aeQuerytransChannel=null, aa=AcAccount [Hash = 305768267, acId=111, acUserId=null, acCustId=null, acAcctType=null, acChannel=null, acChnlGrp=null, acInitVal=null, acAvbBlance=null, acFrezFee=null, acCrtTime=123, acCanCharge=null, acCanWithdraw=null, acCanPay=null, acCanAdjst=null, acCanInvc=null, acState=null, acStateTime=null, acRelationUserId=null, acPubIdtyId=null])

的确不一样了,哈希值也不一样了

总结一下

原型模式便捷了对象的创建,但是细微之处还是有些需要注意的,如果类稍微复杂点,就容易不能达到预期的目标,所以需要深拷贝一下,方式有两种:

  1. 重写clone方法,每个值单独赋值
  2. 通过序列化来处理(有些不想序列化可以在属性前面加transient或者static关键字)

序列化实现深拷贝的原理就是

        序列化是把对象的字面量保存,然后反序列化时候一个个赋值回去,如果里面的属性是其他的类,则需要这个类也实现序列化接口Serializable。同样序列化实现深拷贝也能通过泛型限定是否实现序列化接口。

深拷贝有点不好的就是需要额外的一堆编码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

心若自由何处是束缚

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

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

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

打赏作者

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

抵扣说明:

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

余额充值