詳解設計模式 原型模式
阿新 • • 發佈:2020-06-24
原型模式
原型模式屬於建立型設計模式
定義: 用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件
原型模式通過克隆一個已經存在的物件例項來返回新的例項,而不是通過new去建立物件,多用於建立複雜的或者耗時的例項,因為這種情況下,複製一個已經存在的例項使程式執行更高效;
java中複製物件是通過重寫clone()
實現的,原型類需要實現Cloneable
介面,否則報CloneNotSupportedException
異常
模式類圖
角色
- 抽象原型:Prototype,可以為介面或者抽象類,實現了
Cloneable
介面,重寫了clone()
方法,子類只需實現或整合即可擁有克隆功能 - 具體原型:PrototypeA,PrototypeB,實現/集成了Prototype介面的類,擁有克隆方法
- 工廠模式:原型模式常和工廠模式一起使用,通過 clone 的方法建立一個物件,然後由工廠方法提供給呼叫者
優點
- 效能優良,比new一個物件效能好很多
- 不受物件建構函式的約束
實現
原始碼地址: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()方法是執行的淺拷貝,這個需要在寫程式碼的時候注意一下,淺拷貝是否可以滿足需求
深拷貝
深拷貝的實現方案主要有兩種
- 引用型別也使用clone(),進行clone的時候,對引用型別在呼叫一次clone()方法
- 使用序列化,將物件序列化後在反序列化回來,得到新的物件例項
使用序列化實現以下
/**
* @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
複製程式碼
在使用原型模式的時候一定要理解什麼是淺拷貝和深拷貝,才可以放心的使用原型模式,並且一般都會和工廠模式一起使用