設(shè)計模式之原型模式

一、簡介

原型模式是一種創(chuàng)建型設(shè)計模式,允許使用已有的實例對象作為原型,創(chuàng)建新的對象,無需知道任何如何創(chuàng)建的細(xì)節(jié),也就是“克隆指定對象”。

實現(xiàn)原型模式的思想是讓需要拷貝的原型類必須實現(xiàn)"java.lang.Cloneable"接口,然后重寫Object類中的clone方法。

Cloneable是一個“標(biāo)記接口”,所謂的標(biāo)記接口就是該接口中沒有任何內(nèi)容,但只有實現(xiàn)了Cloneable接口的類才有資格調(diào)用Object類中的clone方法(該方法提供了淺拷貝的功能),否則會拋出“CloneNotSupportedException”異常。

原型模式主要有兩類角色:
1.抽象原型角色((Prototype):
實現(xiàn)Cloneable接口重寫clone方法(可定義為抽象,也可具體實現(xiàn),視情況而定),來說明它有被克隆功能。
2.具體原型(Concrete Prototype)角色
繼承抽象原型角色,則具體原型角色自動具有拷貝功能,這樣節(jié)省代碼量,當(dāng)然也可以讓具體原型角色直接實現(xiàn)實現(xiàn)Cloneable接口。

原型模式的類圖如下:


原型模式.png

二、使用場景

當(dāng)對象的創(chuàng)建非常復(fù)雜,可以使用原型模式快捷的創(chuàng)建對象?;蛘咴谶\(yùn)行過程中不知道對象的具體類型,可使用原型模式創(chuàng)建一個相同類型的對象。

使用原型模式的優(yōu)點在于可以基于原型,快速的創(chuàng)建一個對象,而無需知道創(chuàng)建的細(xì)節(jié)。另外,由于clone方法是由虛擬機(jī)直接復(fù)制內(nèi)存塊執(zhí)行,不會執(zhí)行構(gòu)造方法,避免了初始化需要的時間

三、舉例

public class 原型模式{
    public static void main(String[] args) {
        Car1ConcretePrototype car1_1=new Car1ConcretePrototype("car1",100,new ArrayList<String>(){{add("hello1");}});
        Car1ConcretePrototype car1_2=(Car1ConcretePrototype)car1_1.clone();
        Car2ConcretePrototype car2_1=new Car2ConcretePrototype("car2",200,new ArrayList<String>(){{add("hello2");}});
        Car2ConcretePrototype car2_2=(Car2ConcretePrototype)car2_1.clone2();
        car1_1.show();car1_2.show();
        car2_1.show();car2_2.show();
    }
}
class ICarPrototype implements Cloneable,Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    private int price;
    private ArrayList<String>otherInfo;
    public ICarPrototype(String name, int price, ArrayList<String> otherInfo) {
        this.name = name;
        this.price = price;
        this.otherInfo = otherInfo;
    }
    public String getName() {
        return name;
    }
    public int getPrice() {
        return price;
    }
    public ArrayList<String> getOtherInfo() {
        return otherInfo;
    }
    //深度克隆方式1
    @Override
    protected Object clone() {
        ICarPrototype cloneResult=null;
        try {
            //可以對基本數(shù)據(jù)類型進(jìn)行復(fù)制,即Object中的clone方法實現(xiàn)的只是淺拷貝
            cloneResult=(ICarPrototype)super.clone();
            //下面對每一個復(fù)雜成員對象(如數(shù)組、容器、引用對象等)分別進(jìn)行拷貝,以實現(xiàn)對象的深拷貝
            cloneResult.otherInfo=(ArrayList<String>)this.otherInfo.clone();
        } catch (CloneNotSupportedException e) {
            // TODO 自動生成的 catch 塊
            e.printStackTrace();
        }
        return cloneResult;
    }
    //深度克隆方式2,通過序列化對象實現(xiàn)深拷貝,需要實現(xiàn)Serializable接口
    protected Object clone2() {
        //聲明流對象
        ByteArrayOutputStream bos=null;
        ByteArrayInputStream bis=null;
        ObjectOutputStream oos=null;
        ObjectInputStream ois=null;
        try{
            //創(chuàng)建序列化流
            bos=new ByteArrayOutputStream();
            oos=new ObjectOutputStream(bos);
            //將當(dāng)前對象以對象流的形式輸出
            oos.writeObject(this);
            //創(chuàng)建反序列化流
            bis=new ByteArrayInputStream(bos.toByteArray());
            ois=new ObjectInputStream(bis);
            //將流對象反序列化,從而實現(xiàn)對象的深度拷貝
            return ois.readObject();
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }finally{
            //釋放資源
            try {
                bos.close();bis.close();
                oos.close();ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
class Car1ConcretePrototype extends ICarPrototype{
    private static final long serialVersionUID = 2L;
    public Car1ConcretePrototype(String name, int price,ArrayList<String> otherInfo) {
        super(name, price, otherInfo);
    }
    public void show(){
        System.out.println("**1類型車的信息:**");
        System.out.println("車名:"+getName());
        System.out.println("車價:"+getPrice());
        System.out.println("其他信息:"+getOtherInfo().toString());
    }
    
}
class Car2ConcretePrototype extends ICarPrototype{
    private static final long serialVersionUID =3L;
    public Car2ConcretePrototype(String name, int price,ArrayList<String> otherInfo) {
        super(name, price, otherInfo);
    }
    public void show(){
        System.out.println("##2類型車的信息:##");
        System.out.println("車名:"+getName());
        System.out.println("車價:"+getPrice());
        System.out.println("其他信息:"+getOtherInfo().toString());
    }
    
}

四、擴(kuò)展

  1. 需要注意的是調(diào)用clone方法克隆java對象的時候不會調(diào)用構(gòu)造器。
    這是因為執(zhí)行clone方法的時候是直接從內(nèi)存中去獲取數(shù)據(jù)的,在第一次創(chuàng)建對象的時候就會把數(shù)據(jù)在內(nèi)存保留一份,克隆的時候直接使用就好了
  2. 訪問權(quán)限對原型模式無效
    由于數(shù)據(jù)從內(nèi)存中直接復(fù)制的,所以克隆起來也會直接無視數(shù)據(jù)的訪問權(quán)限,即使對于私有的數(shù)據(jù)也可以獲取復(fù)制過來。

五、參考

原型模式
深度好文:設(shè)計模式之——原型模式
Java Object對象之clone方法
設(shè)計模式之原型模式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容