c#

位置:IT落伍者 >> c# >> 浏览文章

.NET独有的精巧泛型设计模式


发布日期:2019年12月05日
 
.NET独有的精巧泛型设计模式
NET发展史中是具有里程碑意义的一个版本从这个版本NET青出于蓝(Java)而胜于蓝NET 带来的诸多新特性中我认为泛型是最重要没有之一

虽然泛型出现已有多年连Java都早已借鑒引入了泛型(虽然是语法糖)可是用泛型的编程思维方式并没有得到相应的普及一方面是由于过去大量的Framework仍然是在非泛型时代写成的另一方面泛型的设计模式没有得到发展改变的时候该到了

来举一个例子说明这两点我们如果写过网络数据抓取的代码应该熟悉这样的代码

var request = WebRequestCreate as HttpWebRequest;

或者这么写也是一样

var request = HttpWebRequestCreate as HttpWebRequest;

大家可想过为什么每次都要as一下?

类似的情况还有比如做图像处理的弟兄会熟悉

var bm = ImageFromFile(e:\\mejpg) as Bitmap;

var bm = BitmapFromFile(e:\\mejpg) 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:\mejpg;

var bm = BitmapFromFile(path) ;

ConsoleWriteLine(bmPath)

ConsoleWriteLine(bmGetType()Name)

输出结果如下

Path: e:\mejpg

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<TK> where T:TreeNode<TK> 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<RBTreeNodeInt>

{

/// <summary>

/// 树节点颜色是否为红

/// </summary>

public bool IsRed { get; set; }

public override string ToString()

{

return thisValue + + (thisIsRed ? R : B

}

}

这个是AVL树

class AvlTreeNode : TreeNode<AvlTreeNodeInt>

{

/// <summary>

/// 节点的平衡度

/// </summary>

public int Balance { get; set; }

public override string ToString()

{

return Balance: + Balance + Value: + thisValue;

}

}

不但完全符合OCP原则而且再也不需要as来强制转换节点类型了

这肯定不是我的首创其实NET Framework中已经不少这样的设计比如IComparable<T>接口也有不少优秀的框架采用了类似的设计比如大石头同学的ORM框架NewLifeXCode

看上去也很简单吧但是很多人思维还停留在面向对象语言刚诞生的阶段还不习惯用这种设计模式我认为这种写法足够典型和通用足以得上一种设计模式而且是NET特殊优势独特魅力

说到设计模式其实GOF提出的种设计模式多年了已经过时出现了许多新模式(比如并发编程方面参考Wiki Design Pattern)旧有的模式中有的已经包含在NET语言特性中有的模式实现方式已经改头换面尤其在泛型出现后许多模式的实现可以变得简洁许多优雅许多

不要一遍遍炒过去的冷饭设计模式应该与时俱进永远是充满新鲜活力的话题

上一篇:用 C# 编程实现读写Binary

下一篇:VS2005.NET进行三层结构应用程序的开发