Java创建型设计模式 —— 哇,原型设计模式原来是这么玩的

一、引言

欲言又止,

二、克隆羊

假设现在我们有一个对象,需要拷贝新的对象出来,以下代码是最简单粗暴的方式了。 但是如果这个对象有很多属性呢? 那岂不是太麻烦了,针对这种情况就可以使用我们的原型模式来实现。

原型模式是指:用原型实例指定创建对象的种类,并且通过拷贝原型,从而创建新的对象。

原型模式是一种创建型的设计模式,允许一个对象在创建另外一个可定制的对象,无需知道创建的细节。其实也就是说白了把拷贝的具体实现封装在类里面,外部只需要使用对应的方法则可以实现拷贝的效果。

  public static void main(String[] args) {
        
        // 创建一个对象
        Sheep sheep = new Sheep("Tom", 1);

        // 根据上面那个对象,创建新的对象
        Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge());
        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge());

        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);

    }

三、使用原型模式实现克隆羊

针对上面刚刚那个案例,使用原型模式进行改进。

步骤一:在Sheep这个类需要实现Cloneable的接口,其实这个Cloneable是一个标记接口,然后在类中需要重写Object的clone方法,然后通过类调用这个clone方法,从而达到克隆的效果。

如果不实现这个接口,则会抛出CloneNotSupportedException克隆不被支持的异常。

步骤二:重写Object的clone方法,使用时直接调用即可。

/**
 * @Auther: IT贱男
 * @Date: 2019/8/5 15:21
 * @Description: 克隆羊 - 原型模式
 */
@Data
public class Sheep implements Cloneable {

    private String name;
    private int age;


    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Sheep clone() {
        try {
            // 这里默认调用父类的实现方法即可,默认是浅拷贝
            return (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
 public static void main(String[] args) {

        Sheep sheep = new Sheep("Tom", 1);
        // 这里直接调用clone方法即可
        Sheep sheep1 = sheep.clone();
        Sheep sheep2 = sheep.clone();

        System.out.println(sheep == sheep1);
        System.out.println(sheep2 == sheep1);
    }

 

四、理解深拷贝和浅拷贝

浅拷贝

对于基本数据类型的成员变量,浅拷贝会进行值传递,也就是将属性值赋值给新对象。

扫描二维码关注公众号,回复: 10014778 查看本文章

对于成员是引用类型,比如数组、对象等,那么浅拷贝会进行引用传递。也就是将成员等引用值复制一个给新对象,实际上两个对象都是指向同一个实例,在这种情况下修改了一个对象中得值,另外一个对象也会随之而改变。

深拷贝

深拷贝不仅仅复制了所有的基本数据类型,为所有的引用数据类型的成员变量申请存储空间。 也就是说引用成员不再拷贝引用地址,而是整个对象进行拷贝。

 

五、深拷贝两种实现方式

现在我们在Sheep类中新增一个对象叫Friend,并且添加对应的构造方法。

/**
 * @Auther: IT贱男
 * @Date: 2019/8/5 15:21
 * @Description: 克隆羊 - 原型模式
 */
@Data
public class Sheep implements Cloneable {

    private String name;
    private int age;
    private Friend friend;


    public Sheep(String name, int age, Friend friend) {
        this.name = name;
        this.age = age;
        this.friend = friend;
    }

    @Override
    protected Sheep clone() {
        try {
            return (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
 public static void main(String[] args) {

        Sheep sheep = new Sheep("Tom", 1,new Friend("Miqi"));
        // 这里采用的是默认浅拷贝,所以拷贝出来的friend对象是同一个
        Sheep sheep1 = sheep.clone();
        System.out.println(sheep.getFriend() == sheep1.getFriend());

    }

实现方式一:首先我们需要将Friend这个类,实现默认的clone方法,然后重写Sheep中的clone方法。

这种方式虽然可以实现,如果引用对象很多,是不推荐使用的

/**
 * @Auther: IT贱男
 * @Date: 2019/8/20 11:25
 * @Description: 
 */
public class Friend implements Cloneable {

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

    private String name;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
 /**
     * 深拷贝实现方式一
     * @return
     */
    @Override
    protected Sheep clone() {
        try {
            Sheep sheep = (Sheep) super.clone();
            // 这里需要调用friend的clone方法,然后复制给新克隆出来的对象
            sheep.setFriend((Friend) friend.clone());
            return sheep;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

实现方式二:方式一虽然说可以实现,但是大家也看出来了,引用对象需要重新赋值,那如果引用对象很多,那也很麻烦,所以来看看第二种方式,这种方式比较省事。

首先需要将引用对象实现序列化接口,然后采用序列化的方式来实现深拷贝。

/**
 * @Auther: IT贱男
 * @Date: 2019/8/20 11:25
 * @Description:
 */
public class Friend implements Serializable{

    private static final long serialVersionUID = -3019656355622657141L;

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

    private String name;

}
/**
 * @Auther: IT贱男
 * @Date: 2019/8/5 15:21
 * @Description: 克隆羊 - 原型模式
 */
@Data
public class Sheep implements Cloneable, Serializable {

    private static final long serialVersionUID = -270095030076915889L;
    private String name;
    private int age;
    private Friend friend;

    public Sheep(String name, int age, Friend friend) {
        this.name = name;
        this.age = age;
        this.friend = friend;
    }

    /**
     * 使用序列化的方式实现深拷贝
     * @return
     */
    @Override
    protected Sheep clone() {

        Sheep sheep = 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);
            sheep = (Sheep) ois.readObject();

            bos.close();
            oos.close();
            bis.close();
            ois.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return sheep;
    }
}

六、原型模式优缺点

优点:如果需要复制一个新的对象时,可以利用原型模式简化对象的创建过程,同时也可以提高效率。

缺点:需要为每一个类配备一个克隆方法,如果对已有的代码类进行改造,需要修改源码,违背了OCP原则。

 

发布了152 篇原创文章 · 获赞 422 · 访问量 43万+

猜你喜欢

转载自blog.csdn.net/weixin_38111957/article/details/99823797