代码改变世界

ASP.NET Routing中最令人摸不着头脑的设计

2009-08-18 19:47  Jeffrey Zhao  阅读(7333)  评论(59编辑  收藏  举报

您觉得ASP.NET Routing中最令人摸不着头脑的设计是什么?我认为是RouteBase类:

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平台下,一个没有任何实现的,纯粹的抽象类可谓有百害而无一利1。我很怀疑写这段代码的人刚从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这种成熟框架来说,应该做到“无痛”才对。

这次微软真搞笑了。

 

注1:这自然是夸张的做法,抽象类并非没有优势,请关注《再谈抽象类和接口》一文。