.NET独有的精巧泛型设计模式
在.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语言特性中,有的模式实现方式已经改头换面。尤其在泛型出现后,许多模式的实现可以变得简洁许多,优雅许多。
不要一遍遍炒过去的冷饭,设计模式应该与时俱进,永远是充满新鲜活力的话题。
转载于:https://www.cnblogs.com/XmNotes/archive/2012/04/23/2466938.html
.NET独有的精巧泛型设计模式相关推荐
- java i o不会的地方_Java I/O 好复杂,傻傻分不清楚,别担心,我们有线索了。。。...
IO 类图 小帅最近在学Java的IO类库,这么多类看得小帅人头昏眼花,常常是学了这个类,忘了那个类,再过一阵子就全忘了... 每次用到的时候,小帅都要重新读文档,看代码,如此循环,身心疲惫. 小帅没 ...
- 开心网外挂开发之 XML序列化于反序列化
通过昨天的文章我们已经了解了开发一个开心网的外挂所需要的最基本的东本,接下来的工作可以说基本上就上围绕这个基础来进行的,我写这一系列文章主要的目地不仅仅是为了让大家了解怎么开发开心网外挂,最主要的目过 ...
- 打造TypeScript的Visual Studio Code开发环境
打造TypeScript的Visual Studio Code开发环境 本文转自:https://zhuanlan.zhihu.com/p/21611724 作者: 2gua TypeScript是由 ...
- [毕业生的商业软件开发之路]系列文章目录规划
目前[毕业生的商业软件开发之路]系列文章规划出的目录如下,希望靠大家一起完善它,有什么意见欢迎跟帖. 第一章 课程背景 高校毕业生就业问题 高校毕业生就业方式 第二章初入职场 课程说明 关于企业培训 ...
- Java架构师成长之路-100问
1. 进程与线程区别 2. 进程间通信方式 3. 网络七层 4. TCP与UDP区别 5. ajax状态.实现原理 6. ...
- java i= 0001_Java I/O 好复杂,傻傻分不清楚,别担心,我们有线索了。。。
IO 类图 小帅最近在学Java的IO类库,这么多类看得小帅人头昏眼花,常常是学了这个类,忘了那个类,再过一阵子就全忘了... 每次用到的时候,小帅都要重新读文档,看代码,如此循环,身心疲惫. 小帅没 ...
- java的内容好复杂_Java I/O 好复杂,傻傻分不清楚,别担心,我们有线索了……...
IO 类图 小帅最近在学Java的IO类库,这么多类看得小帅人头昏眼花,常常是学了这个类,忘了那个类,再过一阵子就全忘了... 每次用到的时候,小帅都要重新读文档,看代码,如此循环,身心疲惫. 小帅没 ...
- 戴上装饰者模式的眼镜,看透Java I/O
文章目录 IO 类图 看个例子 装饰者模式 OutputStream家族类: 清晰起来了 InputStream家族类: Writer家族类: Reader家族类: 再看个例子 最后的话 IO 类图 ...
- 需求、需求分析、设计 思想
需求.需求分析和设计的概念,对我来说很抽象.在我做了几次相关的工作已后,它们在我的头脑中才逐渐清晰起来.我曾试图从别人的书中,了解它们的真帝,可是大家众说分云,和实际工作都挨不上.通常书里会说:&qu ...
最新文章
- 2020卫星参数表大全_王者荣耀比较秀的名字 2020年比较骚气比较浪的王者荣耀名字大全...
- 个人作业-Alpha项目测试
- MATLAB遍历子文件夹及其下文件
- hive导数据到mysql 自增主键出错_老大问我:“建表为啥还设置个自增 id ?用流水号当主键不正好么?”...
- Python学习教程实用技法:通过公共键对字典列表排序—itemgetter
- 为应用“瘦身”!给 Android 应用开发者的十个建议(转)
- wamp 使用mysql_PHP当中如何使用Wampserver连接到Mysql数据库以及怎样使用
- Windows7环境下用VirtualBox (5.1)上安装Ubuntu 17.10
- 操作系统_图解deepin操作系统安装,体验定制版的国产操作系统
- sql server里执行delete或者update操作产生大量事务日志,导致空间不够,执行失败,能不能设置此类动作时不生成事务日志的...
- 设备\Device\Harddisk1\DR1 有一个不对的区块
- java实现支付宝网页扫码支付
- 高盛发布区块链报告:从理论到实践(中文版)三
- 2023秋招--游卡--游戏客户端--二面面经
- 什么是 Power BI Desktop?
- java语言基础知识(完整版)
- 图形化界面的Xmind用例转excel工具
- 思科 | VLAN 间路由实验(三层交换机)
- Android 设置横屏或竖屏
- 骑马式瘦腿瑜伽 消除肌肉型小腿