2020重新出發(fā)蒿偎,JAVA設(shè)計(jì)模式 之二 原型模式

原型模式(原型設(shè)計(jì)模式)詳解

在有些系統(tǒng)中朽们,存在大量相同或相似對(duì)象的創(chuàng)建問題,如果用傳統(tǒng)的構(gòu)造函數(shù)來創(chuàng)建對(duì)象诉位,會(huì)比較復(fù)雜且耗時(shí)耗資源骑脱,用原型模式生成對(duì)象就很高效,就像孫悟空拔下猴毛輕輕一吹就變出很多孫悟空一樣簡(jiǎn)單苍糠。

原型模式的定義與特點(diǎn)

原型(Prototype)模式的定義如下:用一個(gè)已經(jīng)創(chuàng)建的實(shí)例作為原型叁丧,通過復(fù)制該原型對(duì)象來創(chuàng)建一個(gè)和原型相同或相似的新對(duì)象。在這里岳瞭,原型實(shí)例指定了要?jiǎng)?chuàng)建的對(duì)象的種類拥娄。用這種方式創(chuàng)建對(duì)象非常高效,根本無須知道對(duì)象創(chuàng)建的細(xì)節(jié)瞳筏。

  • 例如稚瘾,Windows 操作系統(tǒng)的安裝通常較耗時(shí),如果復(fù)制就快了很多姚炕。在生活中復(fù)制的例子非常多摊欠,這里不一一列舉了。

原型模式的結(jié)構(gòu)與實(shí)現(xiàn)

由于 Java 提供了對(duì)象的 clone() 方法柱宦,所以用 Java 實(shí)現(xiàn)原型模式很簡(jiǎn)單些椒。

1. 模式的結(jié)構(gòu)

原型模式包含以下主要角色。

  1. 抽象原型類:規(guī)定了具體原型對(duì)象必須實(shí)現(xiàn)的接口掸刊。
  2. 具體原型類:實(shí)現(xiàn)抽象原型類的 clone() 方法免糕,它是可被復(fù)制的對(duì)象。
  3. 訪問類:使用具體原型類中的 clone() 方法來復(fù)制新的對(duì)象。

其結(jié)構(gòu)圖如圖 1 所示说墨。

原型模式的結(jié)構(gòu)圖

? 圖1 原型模式的結(jié)構(gòu)圖

2. 模式的實(shí)現(xiàn)

原型模式的克隆分為淺克隆和深克隆骏全,Java 中的 Object 類提供了淺克隆的 clone() 方法,具體原型類只要實(shí)現(xiàn) Cloneable 接口就可實(shí)現(xiàn)對(duì)象的淺克隆尼斧,這里的 Cloneable 接口就是抽象原型類姜贡。其代碼如下:

//具體原型類
class Realizetype implements Cloneable
{
    Realizetype()
    {
        System.out.println("具體原型創(chuàng)建成功!");
    }
    public Object clone() throws CloneNotSupportedException
    {
        System.out.println("具體原型復(fù)制成功棺棵!");
        return (Realizetype)super.clone();
    }
}
//原型模式的測(cè)試類
public class PrototypeTest
{
    public static void main(String[] args)throws CloneNotSupportedException
    {
        Realizetype obj1=new Realizetype();
        Realizetype obj2=(Realizetype)obj1.clone();
        System.out.println("obj1==obj2?"+(obj1==obj2));
    }
}

程序的運(yùn)行結(jié)果如下:

具體原型創(chuàng)建成功楼咳!
具體原型復(fù)制成功!
obj1==obj2?false

原型模式的應(yīng)用實(shí)例

用原型模式模擬“孫悟空”復(fù)制自己例子烛恤。

分析:孫悟空拔下猴毛輕輕一吹就變出很多孫悟空母怜,這實(shí)際上是用到了原型模式。這里的孫悟空類 SunWukong 是具體原型類缚柏,而 Java 中的 Cloneable 接口是抽象原型類苹熏。

同前面介紹的豬八戒實(shí)例一樣,由于要顯示孫悟空的圖像(點(diǎn)擊此處下載該程序所要顯示的孫悟空的圖片)币喧,所以將孫悟空類定義成面板 JPanel 的子類轨域,里面包含了標(biāo)簽,用于保存孫悟空的圖像杀餐。

另外干发,重寫了 Cloneable 接口的 clone() 方法,用于復(fù)制新的孫悟空史翘。訪問類可以通過調(diào)用孫悟空的 clone() 方法復(fù)制多個(gè)孫悟空枉长,并在框架窗體 JFrame 中顯示。圖 2 所示是其結(jié)構(gòu)圖琼讽。

孫悟空生成器的結(jié)構(gòu)圖

? 圖2 孫悟空生成器的結(jié)構(gòu)圖

程序代碼如下:

import java.awt.*;
import javax.swing.*;
class SunWukong extends JPanel implements Cloneable
{
    private static final long serialVersionUID = 5543049531872119328L;
    public SunWukong()
    {
        JLabel l1=new JLabel(new ImageIcon("src/Wukong.jpg"));
        this.add(l1);   
    }
    public Object clone()
    {
        SunWukong w=null;
        try
        {
            w=(SunWukong)super.clone();
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println("拷貝悟空失敗!");
        }
        return w;
    }
}
public class ProtoTypeWukong
{
    public static void main(String[] args)
    {
        JFrame jf=new JFrame("原型模式測(cè)試");
        jf.setLayout(new GridLayout(1,2));
        Container contentPane=jf.getContentPane();
        SunWukong obj1=new SunWukong();
        contentPane.add(obj1);       
        SunWukong obj2=(SunWukong)obj1.clone();
        contentPane.add(obj2);   
        jf.pack();       
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
    }
}

? 程序的運(yùn)行結(jié)果如圖 3 所示必峰。

孫悟空克隆器的運(yùn)行結(jié)果

? 圖3 孫悟空克隆器的運(yùn)行結(jié)果

用原型模式除了可以生成相同的對(duì)象,還可以生成相似的對(duì)象跨琳,請(qǐng)看以下實(shí)例自点。

原型模式生成“三好學(xué)生”獎(jiǎng)狀桐罕。

分析:同一學(xué)校的“三好學(xué)生”獎(jiǎng)狀除了獲獎(jiǎng)人姓名不同脉让,其他都相同,屬于相似對(duì)象的復(fù)制功炮,同樣可以用原型模式創(chuàng)建溅潜,然后再做簡(jiǎn)單修改就可以了。圖 4 所示是三好學(xué)生獎(jiǎng)狀生成器的結(jié)構(gòu)圖薪伏。

獎(jiǎng)狀生成器的結(jié)構(gòu)圖

圖4 獎(jiǎng)狀生成器的結(jié)構(gòu)圖

程序代碼如下:

public class ProtoTypeCitation
{
    public static void main(String[] args) throws CloneNotSupportedException
    {
        citation obj1=new citation("張三","同學(xué):在2016學(xué)年第一學(xué)期中表現(xiàn)優(yōu)秀滚澜,被評(píng)為三好學(xué)生。","韶關(guān)學(xué)院");
        obj1.display();
        citation obj2=(citation) obj1.clone();
        obj2.setName("李四"); 
        obj2.display();
    }
}
//獎(jiǎng)狀類
class citation implements Cloneable
{
    String name;
    String info;
    String college;
    citation(String name,String info,String college)
    {
        this.name=name;
        this.info=info;
        this.college=college;
        System.out.println("獎(jiǎng)狀創(chuàng)建成功嫁怀!");
    }
    void setName(String name)
    {
        this.name=name;
    }
    String getName()
    {
        return(this.name);
    }
    void display()
    {
        System.out.println(name+info+college);
    }
    public Object clone() throws CloneNotSupportedException
    {
        System.out.println("獎(jiǎng)狀拷貝成功设捐!");
        return (citation)super.clone();
    }
}

程序運(yùn)行結(jié)果如下:

獎(jiǎng)狀創(chuàng)建成功借浊!
張三同學(xué):在2016學(xué)年第一學(xué)期中表現(xiàn)優(yōu)秀,被評(píng)為三好學(xué)生萝招。韶關(guān)學(xué)院
獎(jiǎng)狀拷貝成功蚂斤!
李四同學(xué):在2016學(xué)年第一學(xué)期中表現(xiàn)優(yōu)秀,被評(píng)為三好學(xué)生槐沼。韶關(guān)學(xué)院

原型模式的應(yīng)用場(chǎng)景

原型模式通常適用于以下場(chǎng)景曙蒸。

  • 對(duì)象之間相同或相似,即只是個(gè)別的幾個(gè)屬性不同的時(shí)候岗钩。
  • 對(duì)象的創(chuàng)建過程比較麻煩纽窟,但復(fù)制比較簡(jiǎn)單的時(shí)候。

原型模式的擴(kuò)展

原型模式可擴(kuò)展為帶原型管理器的原型模式兼吓,它在原型模式的基礎(chǔ)上增加了一個(gè)原型管理器 PrototypeManager 類臂港。該類用 HashMap 保存多個(gè)復(fù)制的原型,Client 類可以通過管理器的 get(String id) 方法從中獲取復(fù)制的原型视搏。其結(jié)構(gòu)圖如圖 5 所示趋艘。

帶原型管理器的原型模式的結(jié)構(gòu)圖

? 圖5 帶原型管理器的原型模式的結(jié)構(gòu)圖

原型管理器生成圖形例子

用帶原型管理器的原型模式來生成包含“圓”和“正方形”等圖形的原型,并計(jì)算其面積凶朗。分析:本實(shí)例中由于存在不同的圖形類瓷胧,例如,“圓”和“正方形”棚愤,它們計(jì)算面積的方法不一樣搓萧,所以需要用一個(gè)原型管理器來管理它們,圖 6 所示是其結(jié)構(gòu)圖宛畦。

圖形生成器的結(jié)構(gòu)圖

? 圖6 圖形生成器的結(jié)構(gòu)圖

程序代碼如下:

import java.util.*;
interface Shape extends Cloneable
{
    public Object clone();    //拷貝
    public void countArea();    //計(jì)算面積
}
class Circle implements Shape
{
    public Object clone()
    {
        Circle w=null;
        try
        {
            w=(Circle)super.clone();
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println("拷貝圓失敗!");
        }
        return w;
    }
    public void countArea()
    {
        int r=0;
        System.out.print("這是一個(gè)圓瘸洛,請(qǐng)輸入圓的半徑:");
        Scanner input=new Scanner(System.in);
        r=input.nextInt();
        System.out.println("該圓的面積="+3.1415*r*r+"\n");
    }
}
class Square implements Shape
{
    public Object clone()
    {
        Square b=null;
        try
        {
            b=(Square)super.clone();
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println("拷貝正方形失敗!");
        }
        return b;
    }
    public void countArea()
    {
        int a=0;
        System.out.print("這是一個(gè)正方形,請(qǐng)輸入它的邊長(zhǎng):");
        Scanner input=new Scanner(System.in);
        a=input.nextInt();
        System.out.println("該正方形的面積="+a*a+"\n");
    }
}
class ProtoTypeManager
{
    private HashMap<String, Shape>ht=new HashMap<String,Shape>(); 
    public ProtoTypeManager()
    {
        ht.put("Circle",new Circle());
           ht.put("Square",new Square());
    } 
    public void addshape(String key,Shape obj)
    {
        ht.put(key,obj);
    }
    public Shape getShape(String key)
    {
        Shape temp=ht.get(key);
        return (Shape) temp.clone();
    }
}
public class ProtoTypeShape
{
    public static void main(String[] args)
    {
        ProtoTypeManager pm=new ProtoTypeManager();    
        Shape obj1=(Circle)pm.getShape("Circle");
        obj1.countArea();          
        Shape obj2=(Shape)pm.getShape("Square");
        obj2.countArea();     
    }
}

運(yùn)行結(jié)果如下所示:

這是一個(gè)圓次和,請(qǐng)輸入圓的半徑:3
該圓的面積=28.2735

這是一個(gè)正方形反肋,請(qǐng)輸入它的邊長(zhǎng):3
該正方形的面積=9
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市踏施,隨后出現(xiàn)的幾起案子石蔗,更是在濱河造成了極大的恐慌,老刑警劉巖畅形,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件养距,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡日熬,警方通過查閱死者的電腦和手機(jī)棍厌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人耘纱,你說我怎么就攤上這事敬肚。” “怎么了束析?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵帘皿,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我畸陡,道長(zhǎng)鹰溜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任丁恭,我火速辦了婚禮曹动,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘牲览。我一直安慰自己墓陈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布第献。 她就那樣靜靜地躺著贡必,像睡著了一般。 火紅的嫁衣襯著肌膚如雪庸毫。 梳的紋絲不亂的頭發(fā)上仔拟,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音飒赃,去河邊找鬼利花。 笑死,一個(gè)胖子當(dāng)著我的面吹牛载佳,可吹牛的內(nèi)容都是我干的炒事。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼蔫慧,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼挠乳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起姑躲,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤睡扬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后肋联,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體威蕉,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年橄仍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侮繁,死狀恐怖虑粥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宪哩,我是刑警寧澤娩贷,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站锁孟,受9級(jí)特大地震影響彬祖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜品抽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一储笑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧圆恤,春花似錦突倍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至淡喜,卻和暖如春秕磷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背炼团。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工跳夭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人们镜。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓币叹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親模狭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子颈抚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355