细节决定成败:一个公共类库
CLI缺憾之一:公共体系结构定义
Hierarchical Structure是一种应用极其广泛的数据结构(树结构),其意义完全可以和Collection结构相提并论。可是.Net Framework的CLI提供了Collection却没有提供Hierarchy。绝大部分情况下,你可以通过其它方式(例如配合集合再定义一些接口)来实现,但是不能否认这些实现都很受局限,并且存在一些明显不合理的依赖。我在实现业务逻辑的时候,想通过某种方式通知表现层通过Hierarchy的方式来处理。而表现层是通过一个实现Hierarchical Structure的公共控件实现的(这样的情况在.net下非常普遍),没有理由要依赖我特定的业务层。
我所期望的Hierarchy服务至少应该提供两个接口和一个基类:
- INode:节点,标定节点的标识、上级节点及子节点集合;
- INodeList:节点集合,可以认为是IList的派生接口,除完成IList的所有功能外,还必须负责与父节点的连接。
- NodeCollectionBase:节点集合基类,可以认为是CollectionBase的包装版本,完成集合管理及父节点的标识。
之所以我觉得这个服务应该由CLI提供,很简单的理由,这些结构应用非常普遍,完全应该是一种公共的、基本的依赖。
我设计的Hierarchy服务在Kanas.Hierarchizations命名空间,除了以上接口和基类外,还提供了一个服务类和两个将普通类型包装成Hierarchical结构的接口。
- Hierarchy服务类:提供了大量Hierarchy的算法,例如求宽度、求深度、求子节点数、求级别等等,另外还通过相关的包装器接口生成Hierarchy体系的功能。
- IHierarchizer接口:提供将普通类包装成INode的接口。
- IHierarchizable接口:提供普通类的线型集合实例及IHierarchizer接口的实现。
应用场景:菜单,可视化的树视图,目录体系、组织结构、财务科目、词典类,等等等等。
CLI缺憾之二:可视化框架
.Net提供的PropertyGrid是Visual Studio.net设计时支持的基础,为此,Microsoft为.Net的PropertyGrid提供了极其强大的扩展能力。ComponentModel命名空间下的很多类型及属性都提供了对编辑器、设计器、类型转换器的支持。不过很显然地,其中存在一些设计漏洞,例如没有提供足够的抽象,直接导致了某些不必要的局限。其后果一是所有提供设计时支持的Attribute都几乎只能应用于PropertyGrid,让运行时类型背负设计时支持的包袱显然有种职责不清的嫌疑;其后果二是PropertyGrid只提供了下拉和弹出两种模式的类型编辑,当然目前是够用的,但不能保证任何时候都够用。
现如今我就遇到这个问题。TypeConverterAttribute和TypeEditorAttribute这两个属性就必须进行Web架构的扩展而不是完全依赖WindowsForms。直观的感觉是,设计时特征依赖运行时特征是合理的,相反则不合理。当然,设计时Attribute在运行时没有任何意义,也不会给运行时带来额外的性能问题。可是,为什么不能将设计时Attribute设计成更好的模型?例如可以设计成侵入式或者附加式。
我设计的对象设计时服务在Kanas.ObjectScope命名空间,采用的是附加式策略。当需要在运行时使用类似设计时服务的功能时便可以更灵活地使用。这个服务是基于一个Xml的Schema,将对象的探查或展示方案部署到相应的配置文件中,运行时根据这些配置获得设计时支持。与ComponentModel的设计思想一样,这一套服务应该包括公共定义(契约)、配置提供器(服务)、对象展示及编辑(消费)三部分。
定义部分包括一个XSD规范及其对应的对象模型、一个数据转换器接口和一个编辑器接口。配置提供器包括一个配置阅读器基类(设计成Component以备设计时使用)、三个不同配置方案的实现(分别实现对Xml配置文件、Xml配置资源和全局配置文件节三种配置方式)。对象的展示是表现层的事情,本套服务不需要实现。不过在本套服务中,通过配置的数据转换器接口实现了从对象集合到数据集的自动转换,分别是Kanas.Common命名空间中的TypeBoundDataTable和ListDataTable。两者的区别是:前者不依赖Scope方案,单纯实现ICollection到DataTable的转换;后者是前者的派生,可以通过Scope方案影响DataTable的生成。
当然,这个方案也存在很大的局限,甚至显得非常粗糙。不过就其摆脱消费依赖来说还是有意义的。当然,眼下只实现了对Property的配置,缺乏对实现对invocation及event hook的扩展,也缺乏对更详细的可视化属性(例如ComponentModel中的CategoryAttribute)的定义。
这就是这个简陋的schema: