1. 程式人生 > 程式設計 >詳解設計模式 原型模式

詳解設計模式 原型模式

原型模式

原型模式屬於建立型設計模式

定義: 用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件

原型模式通過克隆一個已經存在的物件例項來返回新的例項,而不是通過new去建立物件,多用於建立複雜的或者耗時的例項,因為這種情況下,複製一個已經存在的例項使程式執行更高效;

java中複製物件是通過重寫clone()實現的,原型類需要實現Cloneable介面,否則報CloneNotSupportedException異常

模式類圖

角色

  1. 抽象原型:Prototype,可以為介面或者抽象類,實現了Cloneable介面,重寫了clone()方法,子類只需實現或整合即可擁有克隆功能
  2. 具體原型:PrototypeA,PrototypeB,實現/集成了Prototype介面的類,擁有克隆方法
  3. 工廠模式:原型模式常和工廠模式一起使用,通過 clone 的方法建立一個物件,然後由工廠方法提供給呼叫者

優點

  1. 效能優良,比new一個物件效能好很多
  2. 不受物件建構函式的約束

實現

原始碼地址:https://github.com/mingyuHub/design-patterns

以獲取筆物件為例子,結合工廠模式講解如何使用原型模式,涉及的類:Pen(抽象類),Pencil(鉛筆),CarbonPen(碳素筆),PenFactory(工廠類)

抽象原型

抽象類,實現了clone()

方法

/**
 * @author: chenmingyu
 * @date: 2019/2/28 09:54
 * @description: 抽象原型角色
 */

@Data
public abstract class Pen implements Cloneable{

    private String name;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super
.clone();
    }
}
複製程式碼
具體原型

Pencil,繼承Pen

/**
 * @author: chenmingyu
 * @date: 2019/2/28 11:27
 * @description: 鉛筆
 */
public Pencil extends Pen{

    Pencilsuper(name);
    }
}
複製程式碼

CarbonPen,繼承了`Pen

/**
 * @author: chenmingyu
 * @date: 2019/2/28 11:29
 * @description: 碳素筆
 */
CarbonPen extends  CarbonPensuper(name);
    }
}
複製程式碼
工廠類

簡單工廠實現

/**
 * @author: chenmingyu
 * @date: 2019/2/28 11:32
 * @description: 筆生產工廠
 */
PenFactory {

    /**
     * 原型類容器
     */

    private static Map<String, Pen> penMap = new Hashtable<>();

    /**
     * 初始化
     */

    static void init() {
        Pen carbonPen = new CarbonPen("碳素筆");
        penMap.put(CarbonPen.class.getName(),carbonPen);
        Pen pencil = new Pencil("鉛筆");
        penMap.put(Pencil.class.getName(),pencil);
    }

    /**
     * 通過複製獲取例項
     * @param className
     * @return
     * @throws CloneNotSupportedException
     */

    static Pen getPen(Class className) throws CloneNotSupportedException{
        Pen cachedShape = penMap.get(className.getName());
        return (Pen) cachedShape.clone();
    }

}
複製程式碼
驗證
main(String[] args){
        PenFactory.init();
        IntStream.range(0,2).forEach(i->{
            try {
                System.out.println(PenFactory.getPen(CarbonPen.class).getClass());
                System.out.println(PenFactory.getPen(Pencil.class).getClass());
                System.out.println("  ... ");
            }catch (CloneNotSupportedException e){
                e.printStackTrace();
            }
        });
    }
複製程式碼

輸出

com.example.design.prototype.CarbonPen
class Pencil
  ... 
Pencil
  ... 
複製程式碼

淺拷貝和深拷貝

淺拷貝:將一個物件複製後,基本型別會被重新建立,引用型別的物件會把引用拷貝過去,實際上還是指向的同一個物件

深拷貝:將一個物件複製後,基本型別和引用型別的物件都會被重新建立

淺拷貝

舉個例子

/**
 * @author: chenmingyu
 * @date: 2019/2/28 14:53
 * @description: 克隆
 */
Clone private CloneA CloneA;

    Clone() {
        this.CloneA = new CloneA();
    }

    protected Clone return (Clone) super.clone();
    }

    CloneA{
    }
}
複製程式碼

驗證

(String[] args) throws CloneNotSupportedException{

    Clone clone = new Clone();
    Clone clone1 = clone.clone();
    System.out.println(clone == clone1);
    System.out.println(clone.getCloneA() == clone1.getCloneA());
}
複製程式碼

輸出

false    
true
複製程式碼

所以clone()方法是執行的淺拷貝,這個需要在寫程式碼的時候注意一下,淺拷貝是否可以滿足需求

深拷貝

深拷貝的實現方案主要有兩種

  1. 引用型別也使用clone(),進行clone的時候,對引用型別在呼叫一次clone()方法
  2. 使用序列化,將物件序列化後在反序列化回來,得到新的物件例項

使用序列化實現以下

/**
 * @author: chenmingyu
 * @date: 2019/2/28 14:53
 * @description: 淺克隆
 */
Cloneable ,Serializable {

    super.clone();
    }

    /**
     * 深拷貝
     * @return
     * @throws CloneNotSupportedException
     */

    deepClonethrows CloneNotSupportedException {
        Clone clone = null;
        try{
            ByteArrayOutputStream baos=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(baos);
            oos.writeObject(this);
            oos.close();
            ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois=new ObjectInputStream(bais);
            //生成新的物件例項
            clone=(Clone)ois.readObject();
            ois.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        return clone;
    }

    CloneA Serializable{
    }
}
複製程式碼

驗證

new Clone();
    Clone clone1 = clone.deepClone();
    System.out.println(clone == clone1);
    System.out.println(clone.getCloneA() == clone1.getCloneA());
}
複製程式碼

輸出

false
複製程式碼

在使用原型模式的時候一定要理解什麼是淺拷貝和深拷貝,才可以放心的使用原型模式,並且一般都會和工廠模式一起使用

相關閱讀

23中設計模式詳解

建立型設計模式:

詳解設計模式 單例模式

詳解設計模式 建造者模式

詳解設計模式 工廠方法

詳解設計模式 抽象工廠