從零開始學設計模式(五)——原型模式(Prototype Pattern)
原型模式
此模式難度係數為 初級 ,由 Gang Of Four 提出。
原型模式是用於建立重複的物件,提高效能。這種模式實現了一個原型介面,該介面用於建立當前物件的克隆。當直接建立物件的代價比較大時,則採用這種模式。
例如一個物件需要在一個高代價的資料庫操作或者遠端連線之後被建立,我們可以快取該物件,在下一個請求時返回它的克隆,在需要的時候更新資料庫或者通知遠端連線,以此來減少資料庫或遠端連線的呼叫。
意圖
用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。
主要解決:在執行期建立和刪除原型。
何時使用:
1、當一個系統應該獨立於它的產品建立,構成和表示時。
2、當要例項化的類是在執行時刻指定時,例如,通過動態裝載。
3、為了避免建立一個與產品類層次平行的工廠類層次時。
4、當一個類的例項只能有幾個不同狀態組合中的一種時。建立相應數目的原型並克隆它們可能比每次用合適的狀態手工例項化該類更方便一些。
如何解決:利用已有的一個原型物件,快速地生成和原型物件一樣的例項。
關鍵程式碼:
1、實現克隆操作,在 JAVA 中實現 Cloneable介面,重寫 clone()方法。在 .NET 中可以使用 Object 類的 MemberwiseClone() 方法來實現物件的淺拷貝或通過序列化的方式來實現深拷貝。
2、原型模式同樣用於隔離類物件的使用者和具體型別(易變類)之間的耦合關係,它同樣要求這些"易變類"擁有穩定的介面。
解釋
現實世界的例子
記得多莉嗎?克隆的羊!讓我們不要談細節,這裡的關鍵點全是克隆
簡而言之
通過克隆基於現有物件來建立新物件
維基百科
原型模式是軟體開發中一種創造性的設計模式。當要建立的物件型別由一個原型例項確定並使用時,該原型例項被克隆以產生新的物件。
簡而言之,它允許你建立一個現有物件的副本,並根據你的需要修改它,而不是從頭開始建立一個物件並設定它
程式示例
在Java中,可以通過實現 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、資源優化場景。
2、類初始化需要消化非常多的資源,這個資源包括資料、硬體資源等。
3、效能和安全要求的場景。
4、通過 new 產生一個物件需要非常繁瑣的資料準備或訪問許可權,則可以使用原型模式。
5、一個物件多個修改者的場景。
6、一個物件需要提供給其他物件訪問,而且各個呼叫者可能都需要修改其值時,可以考慮使用原型模式拷貝多個物件供呼叫者使用
Java中的現例項子
- ofollow,noindex">java.lang.Object#clone()
寫在最後
注意事項:與通過對一個類進行例項化來構造新物件不同的是,原型模式是通過拷貝一個現有物件生成新物件的。
淺拷貝實現 Cloneable介面,重寫clone方法,深拷貝是通過實現 Serializable介面 讀取二進位制流來實現
在實際專案中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,通過 clone 的方法建立一個物件,然後由工廠方法提供給呼叫者。原型模式已經與 Java 融為渾然一體,我們可以隨手拿來使用。
接下來我們就來編寫一個原型模式與工廠方法模式相結合的程式程式碼示例。
我們有一個HeroFactory工廠,這個工廠可以生產不同型別的(獸族、精靈族)Mage(魔法師)、Warlord(軍隊)、Beast(獸王),只是這些型別的產物在工廠中都不是直接 New 出來的,而是採用原型模式通過已有型別物件 clone 出來的。
首先我們畫出這個程式的UML類圖如下:

diagram-prototype
接下來根據類圖,步驟一:編寫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介面和它的實現類:
/** * * 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、 原型角色: 定義用於複製現有例項來生成新例項的方法,上面的例子中Prototype類即是。
2、 具體原型角色: 實現用於複製現有例項來生成新例項的方法,上面的例子中ElfBeast、ElfMage、ElfWarlord、Orc...等都是。
3、 使用者角色: 維護一個登錄檔,並提供一個找出正確例項原型的方法。最後,提供一個獲取新例項的方法,用來委託複製例項的方法生成新例項。上面的例子中HeroFactoryImpl即是。
以上,原型模式我就在這裡介紹完畢,至此設計模式中建立型模式我們已全部學習完畢。
大家可以試著去把工廠模式、抽象工廠模式、單例模式、建造者模式和今天學習的原型模式組合起來使用,每次組合1-2種設計模式來設計你的應用程式吧,自主設計出來你肯定會有意想不到的收穫和體驗的。
下一篇文章我們將學習結構性模式中的 介面卡模式(Adapter Pattern)
碼字不易,各位看官如果喜歡的話,請給點個贊喜歡:heart:,關注下我,我將努力持續不斷的更新