使用Java实现克隆模式(原型模式)

小叮当买了一台新手机,小叮咚看了表示很喜欢,要买一台一模一样的。要怎么实现呢?需要注意的是,这里和单例模式不一样,单例模式只有一台汽车,但这里需要两台手机,但是属性要一样。一个简单粗暴的实现方法:

public class Demo {
    public static void main(String[] args) {
        Phone phone = new Phone("苹果", "星光色", "iPhone 14");
        System.out.println("小叮当买了一台"+phone+phone.hashCode());
        Phone phone1 = new Phone("苹果", "星光色", "iPhone 14");
        System.out.println("小叮咚买了一台"+phone1+phone.hashCode());
    }
}

复制一份代码,也就是把构造函数的参数设置一份一模一样的创建一个新的实例。这个是最简单实现。但是有一个缺点,实际开发中,对象的参数很多,然后买同样一款手机的人也很多,这样我们就要手动复制很多份代码设置超级多的参数,会让代码看着很臃肿。做为一个优雅的程序员,这种重复性的脏话,我们是要想办法避免的。下面我们使用浅克隆来实现自动复制属性。

1-浅克隆:实现Cloneable接口然后继承clone()方法。

public class Phone implements Cloneable {

    //品牌
    private String band;
    //颜色
    private String color;
    //型号
    private String type;

    public Phone(String band, String color, String type) {
        this.band = band;
        this.color = color;
        this.type = type;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Phone{" +
                "band='" + band + '\'' +
                ", color='" + color + '\'' +
                ", type='" + type + '\'' +
                '}';
    }
}

如何使用呢?

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Phone phone = new Phone("苹果", "星光色", "iPhone 14");
        System.out.println("小叮当买了一台"+phone+phone.hashCode());

        Phone clone = (Phone) phone.clone();

        System.out.println("小叮咚买了一台"+clone+clone.hashCode());
    }
}

可以看到使用克隆方法简洁了很多,不用再填构造函数里的参数了。更优雅的实现了我们的需求。

2-浅克隆存在的问题

如果我们在Phone类新增一个属性Owner

    private Owner owner;

 Owner实现如下

public class Owner {
    private String name;

    public Owner(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

再测试一下clone方法

可以看到Phone的子属性Owner没有被复制,指向的是同一个对象实例,实际上,不止自定义的类型会有这个问题,使用Intger等包装类型也会出现则会个问题。如何解决这个问题呢,下面我们用深拷贝来解决这个问题。

3-深拷贝

3-1第一个办法是把Owner也实现Clone接口;并重写Phone的clone方法

public class Owner implements Cloneable {
    private String name;

    public Owner(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

    //重写Phone的clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Phone phone = (Phone) super.clone();
        phone.owner = (Owner) phone.getOwner().clone();
        return phone;
    }

    public Owner getOwner() {
        return owner;
    }

测试一下

 可以看到Owner指向的实例不一样了,说明克隆成功了,但是这样写比较麻烦,每加一个属性就要重写一下clone方法,如果嵌套多层的话,很容易漏写或者其他原因导致出错。下面我们使用第二种深拷贝来实现。

3-2使用序列化实现深拷贝

首先Phone和Owner都要实现Serializable接口,不用实现Cloneable接口了,然后添加serialVersionUID,保证序列号过程中不出错,然后重写Phone中的clone()方法,完整代码如下:

public class Phone implements Serializable {
    //序列化版本号
    private static final long serialVersionUID = 123456789L;

    //品牌
    private String band;
    //颜色
    private String color;
    //型号
    private String type;

    private Owner owner;

    public Phone(String band, String color, String type, Owner owner) {
        this.band = band;
        this.color = color;
        this.type = type;
        this.owner = owner;
    }

    @Override
    protected Object clone(){
        Phone phone = null;
        try(ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            phone = (Phone) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return phone;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "band='" + band + '\'' +
                ", color='" + color + '\'' +
                ", type='" + type + '\'' +
                ", owner=" + owner +
                '}';
    }
}


public class Owner implements Serializable {
    //序列化版本号
    private static final long serialVersionUID = 123456789L;

    private String name;

    public Owner(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Owner{" +
                "name='" + name + '\'' +
                '}' + hashCode();
    }
}

测试一下

hashcode不一致,但是属性一致,说明克隆成功了。 

总结一下:克隆模式在实际生产中用到的比较少,所以我暂时总结不出啥有价值的东西。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值