设计模式-原型模式

153 阅读2分钟

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

定义看起来有点绕口,实际上在Java中,Object的clone()方法就属于原型模式,不妨简单的理解为:原型模式就是用来克隆对象的。

举个例子,比如有一天,周杰伦到奶茶店点了一份不加冰的奶茶,你说我是周杰伦的忠实粉,也要一份跟周杰伦一样的,用程序表示如下:

public class MilkTea { 
    public String type; 
    public boolean ice;
}

下单:

private void order(){
    MilkTea milkTeaOfJay = new MilkTea();
    milkTeaOfJay.type = "原味";
    milkTeaOfJay.ice = false;
    
    MilkTea yourMilkTea = milkTeaOfJay;
}

好像没有什么问题,将周杰伦的奶茶直接赋值到你的奶茶上就行了,看起来我们并不需要clone方法,但是这样真的是复制一份奶茶吗?

当然不是,Java并非基本类型对象的赋值只是传递地址,这样赋值之后,yourMilkTea仍然指向的周杰伦的奶茶,并不会多一份一样的奶茶。

那么我们要怎么做才能点一份一样的奶茶呢?

private void order(){
    MilkTea milkTeaOfJay = new MilkTea();
    milkTeaOfJay.type = "原味";
    milkTeaOfJay.ice = false;
    
    MilkTea yourMilkTea = new MilkTea();
    yourMilkTea.type = "原味";
    yourMilkTea.ice = false;
}

只有这样,yourMilkTea才是new出来的一份全新的奶茶,我们设想一下,如果有一千个粉丝都需要点和周杰伦一样的奶茶,按照现在的写法就需要new一千次,并为每一个新的对象赋值一千次,造成大量的重复。

更糟糕的是,如果周杰伦临时决定加冰,那么粉丝的配置也要跟着修改。

private void order(){
    MilkTea milkTeaOfJay = new MilkTea();
    milkTeaOfJay.type = "原味";
    milkTeaOfJay.ice = true;
    
    MilkTea yourMilkTea = new MilkTea();
    yourMilkTea.type = "原味";
    yourMilkTea.ice = true;
    
    // 将一千个粉丝的 ice 都修改为 true
    ...
}

大批量的修改无疑是非常丑陋的写法,这就是我们需要clone方法的原因。

运用原型模式,在MilkTea中新增clone方法:


public class MilkTea{
    public String type;
    public boolean ice;

    public MilkTea clone(){
        MilkTea milkTea = new MilkTea();
        milkTea.type = this.type;
        milkTea.ice = this.ice;
        return milkTea;
    }
}

下单:

private void order(){
    MilkTea milkTeaOfJay = new MilkTea();
    milkTeaOfJay.type = "原味";
    milkTeaOfJay.ice = false;
    
    MilkTea yourMilkTea = milkTeaOfJay.clone();
    
    // 一千位粉丝都调用 milkTeaOfJay 的 clone 方法即可
    ...
}

这就是原型模式,Java有一个语法糖,让我们并不需要手写clone方法,这个语法糖就是Cloneable接口,我们只要让需要拷贝的类实现这个接口即可。

public class MilkTea implements Cloneable{
    public String type;
    public boolean ice;

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

值得注意的是,Java自带的clone方法是浅拷贝的,也就是说调用此对象的clone方法,只有基本类型的参数会被拷贝一份,非基本类型的参数不会被拷贝一份,而是继续使用传递引用的方式,如果需要实现深拷贝,必须要自己手动修改clone方法才行。