23種設計模式之原型模式


23種設計模式總篇:https://chenmingyu.top/design/

原型模式

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

定義:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象

原型模式通過克隆一個已經存在的對象實例來返回新的實例,而不是通過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(工廠類)

抽象原型

抽象類,實現了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()方法是執行的淺拷貝,這個需要在寫代碼的時候注意一下,淺拷貝是否可以滿足需求

深拷貝

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

  1. 引用類型也使用clone(),進行clone的時候,對引用類型在調用一次clone()方法
  2. 使用序列化,將對象序列化后在反序列化回來,得到新的對象實例

使用序列化實現以下

/**
 * @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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM