C#設計模式學習筆記:(5)原型模式

来源:https://www.cnblogs.com/atomy/archive/2020/01/16/12192763.html
-Advertisement-
Play Games

本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7640873.html,記錄一下學習過程以備後續查用。 一、引言 很多人說原型設計模式會節省機器記憶體,他們說是拷貝出來的對象是原型的複製,不會使用記憶體。我認為這是不對的,因為拷貝出來的每一個對象都是實際 存在的 ...


    本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7640873.html,記錄一下學習過程以備後續查用。

    一、引言

    很多人說原型設計模式會節省機器記憶體,他們說是拷貝出來的對象是原型的複製,不會使用記憶體。我認為這是不對的,因為拷貝出來的每一個對象都是實際

存在的,每個對象都有自己獨立的記憶體地址且會被GC回收。如果就淺拷貝來說,可能會公用一些欄位(引用類型),但深拷貝是不會的。所以說原型設計模式會

提高記憶體使用率是不一定的,具體還要看當時的設計,如果拷貝出來的對象緩存了,每次使用的是緩存的拷貝對象,那就另當別論,再說該模式本身解決的不

是記憶體使用率的問題。

附:淺複製與深複製的區別

淺複製一個對象:

1)如果這個對象(如int age=18 )是值類型,則得到的對象是一個全新的值類型對象(新的記憶體地址);

2)如果這個對象是引用類型(如class Person):

I、這個對象中的值類型(如person.Age=18)是一個全新的值類型對象(新的記憶體地址);

II、這個對象中的引用類型是公用的(同一記憶體地址),當原始引用類型的值變化時,新生成對象的引用類型的值也會跟著變化。

深複製一個對象:

無論之前這個對象是值類型還是引用類型,得到的新對象都是一個全新的對象(新的記憶體地址)。

    在軟體系統中,當創建一個類的實例的過程很昂貴或很複雜,並且我們需要創建多個這樣的類的實例時,如果用new操作符去創建時,會增加創建的複雜度

與客戶代碼的耦合度。如果採用工廠方法模式來創建這樣的實例對象的話,隨著產品類的不斷增加,導致子類的數量不斷增多,也導致了相應工廠類的增加,

系統複雜程度隨之增加,所以此時使用工廠方法模式來封裝類的創建過程並不合適。

    由於每個類的實例都是相同的(這個相同指的是類型相同,但是每個實例的狀態參數會有不同,如果狀態數值也相同就沒意義了),有一個這樣的對象就可

以了。當我們需要多個相同的類實例時,可以通過對原來對象拷貝一份來完成創建,這個思路正是原型模式的實現方式。

    二、原型模式介紹

    原型模式:英文名稱--Prototype Pattern;分類--創建型。

    2.1、動機(Motivate)

    在軟體系統中,經常面臨著“某些結構複雜的對象”的創建工作,由於需求的變化,這些對象經常面臨著劇烈的變化,但是它們卻擁有比較穩定一致的介面。

如何應對這種變化?如何向“客戶程式(使用這些對象的程式)”隔離出“這些易變對象”,從而使得“依賴這些易變對象的客戶程式”不隨著需求改變而改變?

    2.2、意圖(Intent)

    使用原型實例指定創建對象的種類,然後通過拷貝這些原型來創建新的對象。--《設計模式》Gof

    2.3、結構圖(Structure)

    2.4、模式的組成

    從上圖可以看出,在原型模式的結構圖有以下角色:

    1)原型類(Prototype):原型類,聲明一個Clone自身的介面。

    2)具體原型類(ConcretePrototype):實現一個Clone自身的操作。

    在原型模式中,Prototype通常提供一個包含Clone方法的介面,具體的原型ConcretePrototype使用Clone方法完成對象的創建。

    2.5、原型模式的具體實現

    《大話西游之大聖娶親》這部電影,裡面有這樣一個場景:牛魔王使用無敵牛虱大戰至尊寶,至尊寶的應對之策就是--從腦後拔下一撮猴毛,吹了口仙氣,

無數猴子猴孫現身來大戰牛魔王的無敵牛虱。至尊寶的猴子猴孫就是該原型模式的最好體現,至尊寶創建自己的一個副本,不用還要重新孕育五百年,然後出

世、再學藝,最後再來和老牛大戰,假如這樣的話,估計黃花菜都涼了。至尊寶有3根救命猴毛,輕輕一吹,想要多少個自己就有多少個,方便、快捷。

    class Program
    {
        /// <summary>
        /// 抽象原型,定義了原型本身所具有特征和動作,該類型就是至尊寶。
        /// </summary>
        public abstract class Prototype
        {
            //戰鬥--保護師傅
            public abstract void Fight();
            //化緣--不要餓著師傅
            public abstract void BegAlms();

            //吹口仙氣--變一個自己出來
            public abstract Prototype Clone();
        }

        /// <summary>
        /// 具體原型,例如:行者孫A,他只負責與從天界寵物下界的妖怪戰鬥和化緣齋飯食。
        /// </summary>
        public sealed class MonkeyKingPrototype : Prototype
        {
            //戰鬥--保護師傅
            public override void Fight()
            {
                Console.WriteLine("七十二變,集萬千武藝於一身。");
            }
            //化緣--不要餓著師傅
            public override void BegAlms()
            {
                Console.WriteLine("阿彌陀佛!施主,請施捨點飯食。");
            }

            //吹口仙氣--變一個自己出來
            public override Prototype Clone()
            {
                return (MonkeyKingPrototype)MemberwiseClone();
            }
        }

        /// <summary>
        /// 具體原型,例如:孫行者B,他只負責與自然界修煉成妖的妖怪戰鬥和化緣水果。
        /// </summary>
        public sealed class NewskyPrototype : Prototype
        {
            //戰鬥--保護師傅
            public override void Fight()
            {
                Console.WriteLine("七十二變,集萬千武藝於一身。");
            }
            //化緣--不要餓著師傅
            public override void BegAlms()
            {
                Console.WriteLine("阿彌陀佛!施主,請施捨點水果。");
            }

            //吹口仙氣--變一個自己出來
            public override Prototype Clone()
            {
                return (NewskyPrototype)MemberwiseClone();
            }
        }

        static void Main(string[] args)
        {
            #region 原型模式
            Prototype monkeyKing = new MonkeyKingPrototype();
            Prototype monkeyKing1 = monkeyKing.Clone();
            Prototype monkeyKing2 = monkeyKing.Clone();

            Prototype newsky = new NewskyPrototype();
            Prototype newsky1 = newsky.Clone();
            Prototype newsky2 = newsky.Clone();

            //孫行者A打妖怪
            monkeyKing1.Fight();
            //孫行者B去化緣
            newsky2.BegAlms();

            Console.Read();
            #endregion
        }
    }
View Code

    運行結果如下:

    三、原型模式的實現要點

    Prototype模式同樣用於隔離類對象的使用者和具體類型(易變類)之間的耦合關係,它同樣要求這些“易變類”擁有“穩定的介面”。

    Prototype模式對於“如何創建易變類的實體對象”(創建型模式除了Singleton模式以外,都是用於解決創建易變類的實體對象的問題的)採用“原型克隆”的方

法來做,它使得我們可以非常靈活地動態創建“擁有某些穩定介面”的新對象——所需工作僅僅是註冊一個新類的對象(即原型),然後在任何需要的地方不斷

地Clone。

    Prototype模式中的Clone方法可以利用.NET中的Object類的MemberwiseClone()方法或者序列化來實現深拷貝。

    3.1、原型模式的優點

    1)原型模式向客戶隱藏了創建新實例的複雜性。

    2)原型模式允許動態增加或較少產品類。

    3)原型模式簡化了實例的創建結構,工廠方法模式需要有一個與產品類等級結構相同的等級結構,而原型模式不需要這樣。

    4)產品類不需要事先確定產品的等級結構,因為原型模式適用於任何的等級結構。

    3.2、原型模式的缺點

    1)每個類必須配備一個克隆方法。

    2)配備克隆方法需要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不一定很容易,特別當一個類引用不支持串列化的間接對象,或

者引用含有迴圈結構的時候。

    3.3、原型模式的使用場景

    1)資源優化場景

    類初始化需要消化非常多的資源,這個資源包括數據、硬體資源等。

    2)性能和安全要求的場景

    通過new產生一個對象需要非常繁瑣的數據準備或訪問許可權時,則可以使用原型模式。

    3)一個對象多個修改者的場景

    一個對象需要提供給其它對象訪問而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用。在實際項目中,原型模式很少

單獨出現,一般是和工廠方法模式一起出現,通過clone的方法創建一個對象,然後由工廠方法提供給調用者。

    四、.NET中原型模式的實現

    在.NET中,微軟已經為我們提供了原型模式的介面實現,該介面就是ICloneable。其實這個介面就是抽象原型,提供克隆方法,相當於與上面代碼中Prototype

抽象類,其中的Clone()方法實現原型模式。如果想自定義的類具有克隆的功能,首先需要在類定義時實現ICloneable介面的Clone方法。

namespace System
{
    [ComVisible(true)]
    public interface ICloneable
    {
        object Clone();
    }
}

    其實在.NET中實現了ICloneable介面的類有很多,如下圖所示(只截取了部分,可以用ILSpy反編譯工具進行查看):

    五、總結

    到本篇為止,所有的創建型設計模式就寫完了。學習設計模式應該是一個循序漸進的過程,當我們寫代碼的時候不要一上來就用什麼設計模式,而是通過重構

來使用設計模式。

    下麵總結一下創建型的設計模式:

    單例模式解決的是實體對象個數的問題。除了單例模式之外,其它的創建型模式解決的都是new所帶來的耦合關係。工廠方法模式、抽象工廠模式、建造者模

式都需要一個額外的工廠類來負責實例化“易變對象”,而原型模式則是通過原型(一個特殊的工廠類把工廠和實體對象耦合在一起了)來克隆“易變對象”。如果

遇到“易變類”,起初的設計通常從工廠方法模式開始,當遇到更多的複雜變化時,再考慮重構為其他三種工廠模式(抽象工廠模式、建造者模式、原型模式)。

    一般來說,如果可以使用工廠方法模式,那麼一定可以使用原型模式,但是原型模式的使用情況一般是在類比較容易克隆的條件之上。如果是每個類的實現都

比較簡單,只需要實現MemberwiseClone而沒有引用類型的深拷貝,那麼就更加適合了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 先來看下麵一段html: 這個ng-model名稱帶有一定的規律帶有序號。 先來實現數據綁定,從數據取到數據後,為ng-model綁定相對應的值: var c = response.data $scope.Start1 = $filter("jsonDateFormat")(c.Start1, "y ...
  • Github上優秀的.NET Core開源項目的集合。內容包括:庫、工具、框架、模板引擎、身份認證、資料庫、ORM框架、圖片處理、文本處理、機器學習、日誌、代碼分析、教程等。 Github地址:https://github.com/jasonhua95/awesome-dotnet-core ,【a ...
  • 簡介 在這一節,我們將介紹如何在 Silo 和 Client 中獲取Grain及調用Grain Grain獲取方式 從Grain內部獲取: 從Client獲取: 應用 我們在項目中新增一個教室的概念,學生入學需要到教室先報個到才能分配到學號 1.修改 ,新增兩個介面 2.修改 3.在 中新增 4.在 ...
  • SerialDataReceivedEventHandler無反映不要忘記這2屬性賦值。 serialPort1.DtrEnable = true; serialPort1.RtsEnable = true; ...
  • public partial class Form1 : Form { public Form1() { InitializeComponent(); Dog dog = new Dog(); InsertDog(dog); dog.OnAlert(); //Console.WriteLine(); ...
  • C#實現對Excel操作,根據數據的類型不同或者來源不同會放在不同的頁簽中,C#實現添加頁簽代碼如下:(path為文檔保存的地址,dt為要處理的源數據) public void addSheet(string Path, DataTable dt) { var SlDoc = new SLDocum ...
  • 1.ImportData主方法 把傳入為object數組類型,按照下標取出對應的參數,此處為Table和Username public object[] ImportData(object[] Param) { DataTable dt = (DataTable)Param[0]; string m ...
  • 實現把String字元串轉化為In後可用參數代碼: public string StringToList(string aa) { string bb1 = "("; if (!string.IsNullOrEmpty(aa.Trim())) { string[] bb = aa.Split(new ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...