虽然泛型出现已有多年,连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> |
不但完全符合OCP原则,而且再也不需要as来强制转换节点类型了。
这肯定不是我的首创,其实.NET Framework中已经不少这样的设计,比如IComparable<T>接口。也有不少优秀的框架采用了类似的设计,比如大石头同学的ORM框架NewLife.XCode。
看上去也很简单吧,但是很多人思维还停留在面向对象语言刚诞生的阶段,还不习惯用这种设计模式。我认为这种写法足够典型和通用,足以得上一种设计模式,而且是.NET特殊优势,独特魅力。
说到设计模式,其实GOF提出的23种设计模式多年了,已经过时,出现了许多新模式(比如并发编程方面,参考Wiki Design Pattern)。旧有的模式中,有的已经包含在.NET语言特性中,有的模式实现方式已经改头换面。尤其在泛型出现后,许多模式的实现可以变得简洁许多,优雅许多。
不要一遍遍炒过去的冷饭,设计模式应该与时俱进,永远是充满新鲜活力的话题。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
内存数据网格提供商一头扎进Java
10年的时间里,应用性能解决方案提供商Alachisoft一直在用NCache(针对N-Tier和网格计算.NET应用的内存计算和数据网格产品)为.NET社区服务。
-
遇到这样一个问题:通过java service wrapper部署应用,wrapper进程占用的内存会一直升高, 直到把内存吃完应用崩溃,但是这个wrapper
遇到这样一个问题:通过java service wrapper部署应用,wrapper进程占用的内存会一直升高 […]
-
Google App Engine for Java 对于目前中国需要学习吗?
-
前无古人后无来者的Java平台
开发人员一直在致力于保持Java的活力,经过20年后,我们感觉从来没有更好的、更令人激动的时刻如同Java社区一样。