從零開始學設計模式(五)——原型模式(Prototype Pattern)

原型模式

此模式難度系數(shù)為初級潜的,由Gang Of Four提出骚揍。

原型模式是用于創(chuàng)建重復的對象,提高性能啰挪。這種模式實現(xiàn)了一個原型接口信不,該接口用于創(chuàng)建當前對象的克隆。當直接創(chuàng)建對象的代價比較大時亡呵,則采用這種模式抽活。

例如一個對象需要在一個高代價的數(shù)據(jù)庫操作或者遠程連接之后被創(chuàng)建,我們可以緩存該對象锰什,在下一個請求時返回它的克隆下硕,在需要的時候更新數(shù)據(jù)庫或者通知遠程連接,以此來減少數(shù)據(jù)庫或遠程連接的調(diào)用汁胆。

意圖

用原型實例指定創(chuàng)建對象的種類梭姓,并且通過拷貝這些原型創(chuàng)建新的對象。

主要解決:在運行期建立和刪除原型嫩码。

何時使用:
1誉尖、當一個系統(tǒng)應該獨立于它的產(chǎn)品創(chuàng)建,構成和表示時铸题。
2铡恕、當要實例化的類是在運行時刻指定時琢感,例如,通過動態(tài)裝載没咙。
3猩谊、為了避免創(chuàng)建一個與產(chǎn)品類層次平行的工廠類層次時千劈。
4祭刚、當一個類的實例只能有幾個不同狀態(tài)組合中的一種時。建立相應數(shù)目的原型并克隆它們可能比每次用合適的狀態(tài)手工實例化該類更方便一些墙牌。

如何解決:利用已有的一個原型對象涡驮,快速地生成和原型對象一樣的實例。

關鍵代碼:
1喜滨、實現(xiàn)克隆操作捉捅,在 JAVA 中實現(xiàn) Cloneable接口,重寫 clone()方法虽风。在 .NET 中可以使用 Object 類的 MemberwiseClone() 方法來實現(xiàn)對象的淺拷貝或通過序列化的方式來實現(xiàn)深拷貝棒口。
2、原型模式同樣用于隔離類對象的使用者和具體類型(易變類)之間的耦合關系辜膝,它同樣要求這些"易變類"擁有穩(wěn)定的接口无牵。

解釋

現(xiàn)實世界的例子

記得多莉嗎?克隆的羊厂抖!讓我們不要談細節(jié)茎毁,這里的關鍵點全是克隆

簡而言之

通過克隆基于現(xiàn)有對象來創(chuàng)建新對象

維基百科

原型模式是軟件開發(fā)中一種創(chuàng)造性的設計模式。當要創(chuàng)建的對象類型由一個原型實例確定并使用時忱辅,該原型實例被克隆以產(chǎn)生新的對象七蜘。

簡而言之,它允許你創(chuàng)建一個現(xiàn)有對象的副本墙懂,并根據(jù)你的需要修改它橡卤,而不是從頭開始創(chuàng)建一個對象并設置它

程序示例

在Java中,可以通過實現(xiàn)java.lang.Cloneable接口并重寫clone方法來輕易的做到克隆對象

class Sheep implements Cloneable {
  private String name;
  public Sheep(String name) { this.name = name; }
  public void setName(String name) { this.name = name; }
  public String getName() { return name; }
  @Override
  public Sheep clone() throws CloneNotSupportedException {
    return new Sheep(name);
  }
}

然后上面這個Sheep類對象可以被克隆如下:

Sheep original = new Sheep("Jolly");
System.out.println(original.getName()); // Jolly

// Clone and modify what is required
Sheep cloned = original.clone();
cloned.setName("Dolly");
System.out.println(cloned.getName()); // Dolly

應用場景

1损搬、資源優(yōu)化場景碧库。
2、類初始化需要消化非常多的資源场躯,這個資源包括數(shù)據(jù)谈为、硬件資源等。
3踢关、性能和安全要求的場景伞鲫。
4、通過 new 產(chǎn)生一個對象需要非常繁瑣的數(shù)據(jù)準備或訪問權限签舞,則可以使用原型模式秕脓。
5柒瓣、一個對象多個修改者的場景。
6吠架、一個對象需要提供給其他對象訪問芙贫,而且各個調(diào)用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調(diào)用者使用

Java中的現(xiàn)實例子

寫在最后

注意事項:與通過對一個類進行實例化來構造新對象不同的是傍药,原型模式是通過拷貝一個現(xiàn)有對象生成新對象的磺平。
淺拷貝實現(xiàn) Cloneable接口,重寫clone方法拐辽,深拷貝是通過實現(xiàn) Serializable接口 讀取二進制流來實現(xiàn)

在實際項目中拣挪,原型模式很少單獨出現(xiàn),一般是和工廠方法模式一起出現(xiàn)俱诸,通過 clone 的方法創(chuàng)建一個對象菠劝,然后由工廠方法提供給調(diào)用者。原型模式已經(jīng)與 Java 融為渾然一體睁搭,我們可以隨手拿來使用赶诊。

接下來我們就來編寫一個原型模式與工廠方法模式相結合的程序代碼示例。

我們有一個HeroFactory工廠园骆,這個工廠可以生產(chǎn)不同類型的(獸族舔痪、精靈族)Mage(魔法師)、Warlord(軍隊)遇伞、Beast(獸王)辙喂,只是這些類型的產(chǎn)物在工廠中都不是直接New出來的,而是采用原型模式通過已有類型對象clone出來的鸠珠。

首先我們畫出這個程序的UML類圖如下:

diagram-prototype

接下來根據(jù)類圖巍耗,步驟一:編寫Prototype抽象類

/**
 * 
 * Prototype
 *
 */
public abstract class Prototype implements Cloneable {

  public abstract Object copy() throws CloneNotSupportedException;

}

步驟二:編寫繼承Prototype的Beast、Warlord渐排、Mage抽象類

/**
 * 
 * Beast
 *
 */
public abstract class Beast extends Prototype {

  @Override
  public abstract Beast copy() throws CloneNotSupportedException;

}
/**
 * 
 * Mage
 *
 */
public abstract class Mage extends Prototype {

  @Override
  public abstract Mage copy() throws CloneNotSupportedException;

}

/**
 * 
 * Warlord
 *
 */
public abstract class Warlord extends Prototype {

  @Override
  public abstract Warlord copy() throws CloneNotSupportedException;

}

步驟三:編寫精靈族和獸人族具體子類炬太,繼承步驟二定義的抽象類

public class ElfBeast extends Beast {
  
  private String helpType;

  public ElfBeast(String helpType) {
    this.helpType = helpType;
  }

  public ElfBeast(ElfBeast elfBeast) {
    this.helpType = elfBeast.helpType;
  }

  @Override
  public Beast copy() throws CloneNotSupportedException {
    return new ElfBeast(this);
  }

  @Override
  public String toString() {
    return "Elven eagle helps in " + helpType;
  }

}

public class ElfMage extends Mage {

  
  private String helpType;
  
  public ElfMage(String helpType) {
    this.helpType = helpType;
  }

  public ElfMage(ElfMage elfMage) {
    this.helpType = elfMage.helpType;
  }

  @Override
  public ElfMage copy() throws CloneNotSupportedException {
    return new ElfMage(this);
  }

  @Override
  public String toString() {
    return "Elven mage helps in " + helpType;
  }

}

/**
 * 
 * ElfWarlord
 *
 */
public class ElfWarlord extends Warlord {

  private String helpType;
  
  public ElfWarlord(String helpType) {
    this.helpType = helpType;
  }

  public ElfWarlord(ElfWarlord elfWarlord) {
    this.helpType = elfWarlord.helpType;
  }

  @Override
  public ElfWarlord copy() throws CloneNotSupportedException {
    return new ElfWarlord(this);
  }

  @Override
  public String toString() {
    return "Elven warlord helps in " + helpType;
  }

}

public class OrcBeast extends Beast {
  
  private String weapon;

  public OrcBeast(String weapon) {
    this.weapon = weapon;
  }
  
  public OrcBeast(OrcBeast orcBeast) {
    this.weapon = orcBeast.weapon;
  }

  @Override
  public Beast copy() throws CloneNotSupportedException {
    return new OrcBeast(this);
  }

  @Override
  public String toString() {
    return "Orcish wolf attacks with " + weapon;
  }
  

}
//剩下的獸人OrcMage、OrcWarlord與上面的Elf類似編寫即可...

步驟四:編寫HeroFactory接口和它的實現(xiàn)類:

/**
 * 
 * Interface for the factory class.
 * 
 */
public interface HeroFactory {

  Mage createMage();

  Warlord createWarlord();

  Beast createBeast();

}

/**
 * 
 * Concrete factory class.
 * 
 */
public class HeroFactoryImpl implements HeroFactory {

  private Mage mage;
  private Warlord warlord;
  private Beast beast;

  /**
   * Constructor
   */
  public HeroFactoryImpl(Mage mage, Warlord warlord, Beast beast) {
    this.mage = mage;
    this.warlord = warlord;
    this.beast = beast;
  }

  /**
   * Create mage
   */
  public Mage createMage() {
    try {
      return mage.copy();
    } catch (CloneNotSupportedException e) {
      return null;
    }
  }

  /**
   * Create warlord
   */
  public Warlord createWarlord() {
    try {
      return warlord.copy();
    } catch (CloneNotSupportedException e) {
      return null;
    }
  }

  /**
   * Create beast
   */
  public Beast createBeast() {
    try {
      return beast.copy();
    } catch (CloneNotSupportedException e) {
      return null;
    }
  }

}

步驟五:編寫App客戶端類:

public class App {

  private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

  /**
   * Program entry point
   * 
   * @param args command line args
   */
  public static void main(String[] args) {
    HeroFactory factory;
    Mage mage;
    Warlord warlord;
    Beast beast;

    factory = new HeroFactoryImpl(new ElfMage("cooking"), new ElfWarlord("cleaning"), new ElfBeast("protecting"));
    mage = factory.createMage();
    warlord = factory.createWarlord();
    beast = factory.createBeast();
    LOGGER.info(mage.toString());
    LOGGER.info(warlord.toString());
    LOGGER.info(beast.toString());

    factory = new HeroFactoryImpl(new OrcMage("axe"), new OrcWarlord("sword"), new OrcBeast("laser"));
    mage = factory.createMage();
    warlord = factory.createWarlord();
    beast = factory.createBeast();
    LOGGER.info(mage.toString());
    LOGGER.info(warlord.toString());
    LOGGER.info(beast.toString());
  }
}

最后運行App類驯耻,程序輸出如下:

17:04:21.430 [main] INFO com.iluwatar.prototype.App - Elven mage helps in cooking
17:04:21.433 [main] INFO com.iluwatar.prototype.App - Elven warlord helps in cleaning
17:04:21.434 [main] INFO com.iluwatar.prototype.App - Elven eagle helps in protecting
17:04:21.434 [main] INFO com.iluwatar.prototype.App - Orcish mage attacks with axe
17:04:21.434 [main] INFO com.iluwatar.prototype.App - Orcish warlord attacks with sword
17:04:21.434 [main] INFO com.iluwatar.prototype.App - Orcish wolf attacks with laser

原型模式中主要有三個登場角色:
1亲族、原型角色:定義用于復制現(xiàn)有實例來生成新實例的方法,上面的例子中Prototype類即是可缚。
2霎迫、具體原型角色:實現(xiàn)用于復制現(xiàn)有實例來生成新實例的方法,上面的例子中ElfBeast帘靡、ElfMage知给、ElfWarlord、Orc...等都是。
3涩赢、使用者角色:維護一個注冊表戈次,并提供一個找出正確實例原型的方法。最后筒扒,提供一個獲取新實例的方法怯邪,用來委托復制實例的方法生成新實例。上面的例子中HeroFactoryImpl即是花墩。

以上悬秉,原型模式我就在這里介紹完畢,至此設計模式中創(chuàng)建型模式我們已全部學習完畢观游。

大家可以試著去把工廠模式搂捧、抽象工廠模式、單例模式懂缕、建造者模式和今天學習的原型模式組合起來使用,每次組合1-2種設計模式來設計你的應用程序吧王凑,自主設計出來你肯定會有意想不到的收獲和體驗的搪柑。

下一篇文章我們將學習結構性模式中的適配器模式(Adapter Pattern)

碼字不易,各位看官如果喜歡的話索烹,請給點個喜歡??工碾,關注下我,我將努力持續(xù)不斷的更新

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末百姓,一起剝皮案震驚了整個濱河市渊额,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垒拢,老刑警劉巖旬迹,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異求类,居然都是意外死亡奔垦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門尸疆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椿猎,“玉大人,你說我怎么就攤上這事寿弱》该撸” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵症革,是天一觀的道長筐咧。 經(jīng)常有香客問我,道長地沮,這世上最難降的妖魔是什么嗜浮? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任羡亩,我火速辦了婚禮,結果婚禮上危融,老公的妹妹穿的比我還像新娘畏铆。我一直安慰自己,他們只是感情好吉殃,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布辞居。 她就那樣靜靜地躺著,像睡著了一般蛋勺。 火紅的嫁衣襯著肌膚如雪瓦灶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天抱完,我揣著相機與錄音贼陶,去河邊找鬼。 笑死巧娱,一個胖子當著我的面吹牛碉怔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播禁添,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼撮胧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了老翘?” 一聲冷哼從身側(cè)響起芹啥,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铺峭,沒想到半個月后墓怀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡逛薇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年捺疼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片永罚。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡啤呼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出呢袱,到底是詐尸還是另有隱情官扣,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布羞福,位于F島的核電站惕蹄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望芜飘。 院中可真熱鬧,春花似錦棒旗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至餐曹,卻和暖如春逛拱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背台猴。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工朽合, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卿吐。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓旁舰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嗡官。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容