23種設計模式總篇:https://chenmingyu.top/design/
原型模式
原型模式屬於創建型設計模式
定義:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象
原型模式通過克隆一個已經存在的對象實例來返回新的實例,而不是通過new去創建對象,多用於創建復雜的或者耗時的實例,因為這種情況下,復制一個已經存在的實例使程序運行更高效;
java中復制對象是通過重寫 clone()
實現的,原型類需要實現Cloneable
接口,否則報CloneNotSupportedException
異常
模式類圖
角色
- 抽象原型:Prototype,可以為接口或者抽象類,實現了
Cloneable
接口,重寫了clone()
方法,子類只需實現或集成即可擁有克隆功能 - 具體原型:PrototypeA,PrototypeB,實現/集成了Prototype接口的類,擁有克隆方法
- 工廠模式:原型模式常和工廠模式一起使用,通過 clone 的方法創建一個對象,然后由工廠方法提供給調用者
優點
- 性能優良,比new一個對象性能好很多
- 不受對象構造函數的約束
模式代碼實現
源碼地址:https://github.com/mingyuHub/design-patterns
以獲取筆對象為例子,結合工廠模式講解如何使用原型模式,涉及的類:Pen
(抽象類),Pencil
(鉛筆),CarbonPen
(碳素筆),PenFactory
(工廠類)
抽象原型
抽象類,實現了Cloneable
接口,重寫了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 class Pencil extends Pen{
public Pencil(String name) {
super(name);
}
}
CarbonPen
,繼承了`Pen
/**
* @author: chenmingyu
* @date: 2019/2/28 11:29
* @description: 碳素筆
*/
public class CarbonPen extends Pen{
public CarbonPen(String name) {
super(name);
}
}
工廠類
簡單工廠實現
/**
* @author: chenmingyu
* @date: 2019/2/28 11:32
* @description: 筆生產工廠
*/
public class PenFactory {
/**
* 原型類容器
*/
private static Map<String, Pen> penMap = new Hashtable<>();
/**
* 初始化
*/
public 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
*/
public static Pen getPen(Class className) throws CloneNotSupportedException{
Pen cachedShape = penMap.get(className.getName());
return (Pen) cachedShape.clone();
}
}
驗證
public static void 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();
}
});
}
輸出
class com.example.design.prototype.CarbonPen
class com.example.design.prototype.Pencil
...
class com.example.design.prototype.CarbonPen
class com.example.design.prototype.Pencil
...
淺拷貝和深拷貝
淺拷貝:將一個對象復制后,基本類型會被重新創建,引用類型的對象會把引用拷貝過去,實際上還是指向的同一個對象
深拷貝:將一個對象復制后,基本類型和引用類型的對象都會被重新創建
淺拷貝
舉個例子
/**
* @author: chenmingyu
* @date: 2019/2/28 14:53
* @description: 克隆
*/
@Data
public class Clone implements Cloneable{
private CloneA CloneA;
public Clone() {
this.CloneA = new CloneA();
}
@Override
protected Clone clone() throws CloneNotSupportedException {
return (Clone) super.clone();
}
class CloneA{
}
}
驗證
public static void main(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: 淺克隆
*/
@Data
public class Clone implements Cloneable ,Serializable {
private CloneA CloneA;
public Clone() {
this.CloneA = new CloneA();
}
@Override
protected Clone clone() throws CloneNotSupportedException {
return (Clone) super.clone();
}
/**
* 深拷貝
* @return
* @throws CloneNotSupportedException
*/
protected Clone deepClone() throws 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;
}
class CloneA implements Serializable{
}
}
驗證
public static void main(String[] args) throws CloneNotSupportedException{
Clone clone = new Clone();
Clone clone1 = clone.deepClone();
System.out.println(clone == clone1);
System.out.println(clone.getCloneA() == clone1.getCloneA());
}
輸出
false
false
在使用原型模式的時候一定要理解什么是淺拷貝和深拷貝,才可以放心的使用原型模式,並且一般都會和工廠模式一起使用