- public abstract class RouteBase
- {
- protected RouteBase() { }
- public abstract RouteData GetRouteData(HttpContextBase httpContext);
- public abstract VirtualPathData GetVirtualPath(
- RequestContext requestContext,
- RouteValueDictionary values);
- }
它为什么是一个没有任何实现的抽象类,而不是一个接口(如下)?
- public interface IRoute
- {
- RouteData GetRouteData(HttpContextBase httpContext);
- VirtualPathData GetVirtualPath(
- RequestContext requestContext,
- RouteValueDictionary values);
- }
这样做难道不更漂亮一些吗?这样代码中都可以使用IRoute类型,避免RouteBase这种令人反感的命名出现(个人感觉,不知道有没有同意的群众)。退一步说,命名上的“美感”是小事……但是抽象类在.NET平台中就产生了一个非常严重的限制:一个类无法继承多个基类。因此,在.NET平台上总是更倾向于使用接口,而不是抽象类。
但是接口里不可以有任何实现,那么可复用的功能又放在哪里比较合适呢?《Framework Design Guildlines》告诉我们:在一个类库中,***为接口定义一个默认实现,这样也是开发人员进行“扩展”的一个“参考”。也就是说,如果真有什么需要复用的实现,我们完全可以这么做:
- public abstract class RouteBase : IRoute
- {
- // reusable implementations
- }
- public class Route : RouteBase
- {
- // concrete implementations
- }
事实上,.NET平台上有许多类库也遵循了这个做法。一个典型的做法便是ASP.NET AJAX框架的Extender模型:
- public interface IExtenderControl { }
- public abstract class ExtenderControl : Control, IExtenderControl { }
甚至在ASP.NET AJAX Control Tookit项目中,还有更进一步的扩展:
- public abstract class ExtenderControlBase : ExtenderControl { }
- public class AnimationExtenderControlBase : ExtenderControlBase { }
- public class AutoCompleteExtender : AnimationExtenderControlBase { }
看来微软在项目团队内部推广《Framework Design Guidelines》还不够彻底。
在.NET平台下,一个没有任何实现的,纯粹的抽象类可谓有百害而无一利。我很怀疑写这段代码的人刚从C++切换到C#——但是ASP.NET Routing中其实也有接口(如IRouteConstraint),为什么作者自己没有意识到,也没有人提出不同意见呢?微软开发团队应该有着严格的Code Review过程,怎么会让这样的代码正式发布?要知道一个接口一旦公开,就不可以删除了。也就是说,微软很难弥补这个错误。
如果是方法名不好,或者职责有些不明确,这样还可以在旧方法上添加ObsoleteAttribute(这样编译器便会提示用户这个方法已经过期),并且将旧方法的调用委托给新的实现。例如:
- public abstract class CodeDomProvider : Component
- {
- [Obsolete(
- "Callers should not use the ICodeCompiler interface and should
- instead use the methods directly on the CodeDomProvider class.
- Those inheriting from CodeDomProvider must still implement this
- interface, and should exclude this warning or also obsolete this
- method.")]
- public abstract ICodeCompiler CreateCompiler();
- [Obsolete(
- "Callers should not use the ICodeParser interface and should
- instead use the methods directly on the CodeDomProvider class.
- Those inheriting from CodeDomProvider must still implement this
- interface, and should exclude this warning or also obsolete this
- method.")]
- public virtual ICodeParser CreateParser();
- ...
- }
可是,现在的问题是一个“类”,而这个类已经无处不在了,例如在RouteData中有一个属性Route,它便是RouteBase类型——如果将其修改为IRoute接口,那么至少也需要项目重新编译之后才能够“升级”。而作为一个公开类库,尤其是.NET这种成熟框架来说,应该做到“无痛”才对。
本文来自赵劼的博客园文章《ASP.NET Routing中最令人摸不着头脑的设计》
【编辑推荐】