设计模式--克隆模式(原型模式)

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

克隆模式的要点是:地址不同,属性相同!

 

如果一个对象有四个属性,那么我们可以直接通过new实例化对象,但是如果对象有四十个属性呢?这就是克隆模式发挥光和热的时候了。

首先假定一个商品类,如下:

package Clone;

public class Goods {
    private String code;
    private Integer price;
    private String name;

    public Goods() {
    }

    public Integer getPrice() {
        return price;
    }

    public String getCode() {
        return code;
    }

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

    public void setPrice(Integer price) {
        this.price = price;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }
}

然后我们有两个商品,一个是头铁NB到发光的 “康帅傅牌方便面” 另一个是普普通通的 “康师傅方便面”,如下:

package Clone;
public class Main {
    public static void main(String[] args){
        Goods goods1=new Goods();
        goods1.setCode("A001");
        goods1.setName("康帅傅牌方便面");
        goods1.setPrice(3);


        Goods goods2=goods1;
        goods2.setCode("A002");
        goods2.setName("康师傅方便面");

//输出
        System.out.println(goods1.getCode());
        System.out.println(goods2.getCode());
        System.out.println(goods1.getName());
        System.out.println(goods2.getName());
        System.out.println(goods1.getPrice());
        System.out.println(goods2.getPrice());

    }
}

这里我们通过直接复制的方式获得了两个实例,那么最后的运行结果如何呢?

A002
A002
康师傅方便面
康师傅方便面
3
3

毫不意外,两个实例的输出结果都是一样的,即使我们在对goods2实例的属性进行了修改。由此可见,这并不符合克隆模式的要求,因为goods2和goods1的内存地址都特么是一样的(这也是为什么修改了goods2,goods1也莫名其妙被改变的原因,学习过C++的同学可以很容易理解)。

解决办法是什么呢?首先介绍浅拷贝,如下:

package Clone;

public class Goods implements Cloneable {
    private String code;
    private Integer price;
    private String name;

    public Goods() {
    }

    public Integer getPrice() {
        return price;
    }

    public String getCode() {
        return code;
    }

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

    public void setPrice(Integer price) {
        this.price = price;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    protected Goods clone() {
        try {
            return (Goods) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getName() {
        return name;
    }
}

A001
A002
康帅傅牌方便面
康师傅方便面
3
3

clone方法是通过实现Cloneable接口得到的,通过这个方法,我们可以得到一份浅拷贝的goods1。什么是浅拷贝,就是对象的所有属性都与拷贝后的对象相同,但是浅拷贝不会拷贝更深层次的东西。也就意味着如果你的某一属性是一个对象,那么浅拷贝将直接拷贝其地址,而不是新建一个对象。

package Clone;

public class Cover {
    private String address;
    private String filename;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }
}
package Clone;

import Test.C;

public class Main {
    public static void main(String[] args){
        Goods goods1=new Goods();
        goods1.setCode("A001");
        goods1.setName("康帅傅牌方便面");
        goods1.setPrice(3);
        goods1.getCover().setAddress("D:/image/");
        goods1.getCover().setFilename("A001.jpg");


        Goods goods2=goods1.clone();//改动在这里

        goods2.getCover().setFilename("A002");
        goods2.getCover().setAddress("E:/ABC/");
        //输出
        goods2.setCode("A002");
        goods2.setName("康师傅方便面");
        System.out.println(goods1.getCode());
        System.out.println(goods2.getCode());
        System.out.println(goods1.getName());
        System.out.println(goods2.getName());
        System.out.println(goods1.getPrice());
        System.out.println(goods2.getPrice());
        System.out.println(goods1.getCover().getAddress());
        System.out.println(goods1.getCover().getFilename());
        System.out.println(goods2.getCover().getAddress());
        System.out.println(goods2.getCover().getFilename());

    }
}

输出结果:

A001
A002
康帅傅牌方便面
康师傅方便面
3
3
E:/ABC/
A002
E:/ABC/
A002

深拷贝方案:对特殊属性单独进行拷贝

package Clone;

public class Cover implements Cloneable {
    private String address;
    private String filename;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    @Override
    public Cover clone() {
        try {
            return (Cover) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
package Clone;

public class Goods implements Cloneable {
    private String code;
    private Integer price;
    private String name;
    private Cover cover=new Cover();

    public Goods() {
    }

    public Integer getPrice() {
        return price;
    }

    public String getCode() {
        return code;
    }

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

    public void setPrice(Integer price) {
        this.price = price;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public Goods clone() {
        Goods goods=null;
        try {
            goods=(Goods) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        assert goods != null;
        this.cover= goods.cover.clone();
        return goods;
    }

    public String getName() {
        return name;
    }

    public Cover getCover() {
        return cover;
    }

    public void setCover(Cover cover) {
        this.cover = cover;
    }
}

 

A001
A002
康帅傅牌方便面
康师傅方便面
3
3
D:/image/
A001.jpg
E:/ABC/
A002

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值