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
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...