.NET獨有的精巧泛型設計模式

来源:http://www.cnblogs.com/shouce/archive/2016/05/30/5541061.html
-Advertisement-
Play Games

在.NET發展史中,2.0是具有里程碑意義的一個版本。從這個版本,.NET青出於藍(Java),而勝於藍。在.NET 2.0帶來的諸多新特性中,我認為泛型是最重要,沒有之一。 雖然泛型出現已有多年,連Java都早已借鑒引入了泛型(雖然是語法糖),可是用泛型的編程思維方式並沒有得到相應的普及。一方面是 ...


在.NET發展史中,2.0是具有里程碑意義的一個版本。從這個版本,.NET青出於藍(Java),而勝於藍。在.NET 2.0帶來的諸多新特性中,我認為泛型是最重要,沒有之一。

雖然泛型出現已有多年,連Java都早已借鑒引入了泛型(雖然是語法糖),可是用泛型的編程思維方式並沒有得到相應的普及。一方面是由於過去大量的Framework仍然是在非泛型時代寫成的,另一方面泛型的設計模式沒有得到發展,改變的時候該到了。

來舉一個例子說明這兩點。我們如果寫過網路數據抓取的代碼,應該熟悉這樣的代碼:

var request = WebRequest.Create("http://www.cnblogs.com/") as HttpWebRequest;

 

或者這麼寫,也是一樣:

var request = HttpWebRequest.Create("http://www.cnblogs.com/") as HttpWebRequest;

 

大家可想過,為什麼每次都要as一下?

類似的情況還有,比如做圖像處理的弟兄會熟悉:

var bm = Image.FromFile("e:\\me.jpg") as Bitmap;

 和

var bm = Bitmap.FromFile("e:\\me.jpg") as Bitmap;

 我想過,但沒想明白。上面兩種寫法,都是調用父類的工廠方法,實際返回了一個子類的實例。顯然,即使不瞭解OCP,憑直覺也應該想到,父類的實現中不應該被子類所決定。寫WebRequest和Image的前輩可能也覺得直接返回子類實例不妥,所以陰險地把方法簽名的返回類型改成了父類。

雖然這種行徑值得嚴重鄙視。但.NET程式員大都是人云亦云,照葫蘆畫瓢的好學生,所以這個問題多年了也沒有修改。

理想的設計應該是這樣:父類的每個子類,都有獨立的工廠方法,返回其自身的實例。這樣做法,在泛型出現前非常笨拙,得不償失,但有了泛型,就可以精巧地實現。

以模擬Image類為例,Image和BitMap實現如下:

class Image<T> where T:Image<T>, new()
{
    public string Path { get; set; }

    public static T FromFile(string path)
    {
        return new T() { Path = path };
    }
}

class Bitmap:Image<Bitmap>
{
}

 

Image自身的工廠方法,就沒有存在的必要了。

可以簡單地測試一下:

var path = @"e:\me.jpg";
var bm = Bitmap.FromFile(path); ;

Console.WriteLine(bm.Path);
Console.WriteLine(bm.GetType().Name);

 

輸出結果如下:

Path: e:\me.jpg
Type: Bitmap

為了讓大家更熟悉一下,再舉一個實現數據結構中的二叉樹作例子。

傳統的樹節點類,無論無論C/C++/Java都是類似這樣:

class TreeNode
{
    public TreeNode LeftChild { get; set; }

    public TreeNode RightChild { get; set; }

    public TreeNode Parent { get; set; }

    public int Value { get; set; }
}

 

大家知道,二叉樹又分好幾種,AVL樹、B樹、紅黑樹等等。實現特殊的二叉樹數據結構,勢必要繼承TreeNode。由於樹節點的類型中,有類型為基類的成員,所以在子類操作這些成員時,往往也要強制轉換類型,這比Image和WebRequest的例子,只在實例創建時轉換類型還麻煩。

這就該泛型模式一顯身手的好機會了,請看其父類型的實現:

/// <typeparam name="T">Type of the node.</typeparam>
/// <typeparam name="K">Type of the node value.</typeparam>
class TreeNode<T,K> where T:TreeNode<T,K> where K: IComparable<K>
{
    public T LeftChild { get; set; }

    public T RightChild { get; set; }

    public T Parent { get; set; }

    public K Value { get; set; }
}

 

之後,實現任何一種特殊二叉樹結構,比如RBTreeNode代表紅黑樹節點,可以這樣:

class RBTreeNode : TreeNode<RBTreeNode,Int32>
{
    /// <summary>
    /// 樹節點顏色,是否為紅。
    /// </summary>
    public bool IsRed { get; set; }

    public override string ToString()
    {
        return this.Value + "," + (this.IsRed ? "R" : "B");
    }
}

 

這個是AVL樹:

class AvlTreeNode : TreeNode<AvlTreeNode,Int32>
{
    /// <summary>
    /// 節點的平衡度
    /// </summary>
    public int Balance { get; set; }

    public override string ToString()
    {
        return "Balance: " + Balance + ", Value: " + this.Value;
    }
}

 

不但完全符合OCP原則,而且再也不需要as來強制轉換節點類型了。

這肯定不是我的首創,其實.NET Framework中已經不少這樣的設計,比如IComparable<T>介面。也有不少優秀的框架採用了類似的設計,比如大石頭同學的ORM框架NewLife.XCode。

看上去也很簡單吧,但是很多人思維還停留在面向對象語言剛誕生的階段,還不習慣用這種設計模式。我認為這種寫法足夠典型和通用,足以得上一種設計模式,而且是.NET特殊優勢,獨特魅力。

說到設計模式,其實GOF提出的23種設計模式多年了,已經過時,出現了許多新模式(比如併發編程方面,參考Wiki Design Pattern)。舊有的模式中,有的已經包含在.NET語言特性中,有的模式實現方式已經改頭換面。尤其在泛型出現後,許多模式的實現可以變得簡潔許多,優雅許多。

不要一遍遍炒過去的冷飯,設計模式應該與時俱進,永遠是充滿新鮮活力的話題。


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

-Advertisement-
Play Games
更多相關文章
  • grep是UNIX和LINUX中使用最廣泛的命令之一。grep允許對文本文件進行模式查找。如果找到匹配模式, grep列印包含模式的所有行。grep支持基本正則表達式,也支持其擴展集。grep有三種變形,即: grep:標準grep命令,這裡主要討論此格式; Egrep:等同於grep -E,擴展g ...
  • 算術運算符 + - * / % 表示加減乘除和取餘運算+= -= *= /= 同 C 語言中的含義 位操作符 > >>= 表示位左右移一位操作& &= | |= 表示按位與、位或操作~ ! 表示非操作^ ^= 表示異或操作 關係運算符 = == != 表示大於、小於、大於等於、小於等於、等於、不等於 ...
  • OSI 七層模型通過七個層次化的結構模型使不同的系統不同的網路之間實現可靠的通訊,因此其最主要的功能就是幫助不同類型的主機實現數據傳輸 。 OSI 七層模型通過七個層次化的結構模型使不同的系統不同的網路之間實現可靠的通訊,因此其最主要的功能就是幫助不同類型的主機實現數據傳輸 。 完成中繼功能的節點通 ...
  • 上一篇我們已經可以獲取各種FileHandler的實例和對應的元數據。本篇,我們做一個稍微完整的文件管理器。 1、修改介面IFileHandler,傳入文件名 2、修改具體的FileHandler。 3、修改主函數 運行結果: 可以看到,對每一個具體的文件,均找到了正確的處理實例進行處理。avi文件 ...
  • 首先我們都知道引用類型預設值都是null,而值類型的預設值都有非null。為什麼引用類型可以為空?因為引用類型變數都是保存一個對象的地址引用(就像一個url對應一個頁面),而引用類型值為null的時候是變數值指向了一個空引用(如同一個空的url)那為什麼值不能有空值呢?其實很簡單,因為如int值範圍... ...
  • 稍微有一定複雜性的系統,多級菜單都是一個必備組件。 本篇專題講述如何生成動態多級菜單的通用做法。 我們不用任何第三方的組件,完全自己構建靈活通用的多級菜單。 需要達成的效果:容易復用,可以根據model動態產生。 文章提綱 概述要點 && 理論基礎 詳細步驟 一、分析多級目錄的html結構 二、根據 ...
  • 首先要導入對命名空間 using System.Runtime.InteropServices; 的引用 [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct TokPriv1Luid { public int Count; ...
  • 全文包括如下三部分內容: 方式一、發佈網站至預設的IIS路徑下 方式二、發佈網站至指定的IIS路徑下 註:發佈過程中可能出現的錯誤信息及解決方法 方式一、發佈網站至預設的IIS路徑下 1)發佈環境:Windows 7 旗艦版 + IIS 7.5 + VS2010 + ASP.Net WebForm ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...