分析ASP.NET Routing最令人疑惑的設(shè)計(jì)
- public abstract class RouteBase
- {
- protected RouteBase() { }
- public abstract RouteData GetRouteData(HttpContextBase httpContext);
- public abstract VirtualPathData GetVirtualPath(
- RequestContext requestContext,
- RouteValueDictionary values);
- }
它為什么是一個(gè)沒有任何實(shí)現(xiàn)的抽象類,而不是一個(gè)接口(如下)?
- public interface IRoute
- {
- RouteData GetRouteData(HttpContextBase httpContext);
- VirtualPathData GetVirtualPath(
- RequestContext requestContext,
- RouteValueDictionary values);
- }
這樣做難道不更漂亮一些嗎?這樣代碼中都可以使用IRoute類型,避免RouteBase這種令人反感的命名出現(xiàn)(個(gè)人感覺,不知道有沒有同意的群眾)。退一步說,命名上的“美感”是小事……但是抽象類在.NET平臺(tái)中就產(chǎn)生了一個(gè)非常嚴(yán)重的限制:一個(gè)類無法繼承多個(gè)基類。因此,在.NET平臺(tái)上總是更傾向于使用接口,而不是抽象類。
但是接口里不可以有任何實(shí)現(xiàn),那么可復(fù)用的功能又放在哪里比較合適呢?《Framework Design Guildlines》告訴我們:在一個(gè)類庫(kù)中,***為接口定義一個(gè)默認(rèn)實(shí)現(xiàn),這樣也是開發(fā)人員進(jìn)行“擴(kuò)展”的一個(gè)“參考”。也就是說,如果真有什么需要復(fù)用的實(shí)現(xiàn),我們完全可以這么做:
- public abstract class RouteBase : IRoute
- {
- // reusable implementations
- }
- public class Route : RouteBase
- {
- // concrete implementations
- }
事實(shí)上,.NET平臺(tái)上有許多類庫(kù)也遵循了這個(gè)做法。一個(gè)典型的做法便是ASP.NET AJAX框架的Extender模型:
- public interface IExtenderControl { }
- public abstract class ExtenderControl : Control, IExtenderControl { }
甚至在ASP.NET AJAX Control Tookit項(xiàng)目中,還有更進(jìn)一步的擴(kuò)展:
- public abstract class ExtenderControlBase : ExtenderControl { }
- public class AnimationExtenderControlBase : ExtenderControlBase { }
- public class AutoCompleteExtender : AnimationExtenderControlBase { }
看來微軟在項(xiàng)目團(tuán)隊(duì)內(nèi)部推廣《Framework Design Guidelines》還不夠徹底。
在.NET平臺(tái)下,一個(gè)沒有任何實(shí)現(xiàn)的,純粹的抽象類可謂有百害而無一利。我很懷疑寫這段代碼的人剛從C++切換到C#——但是ASP.NET Routing中其實(shí)也有接口(如IRouteConstraint),為什么作者自己沒有意識(shí)到,也沒有人提出不同意見呢?微軟開發(fā)團(tuán)隊(duì)?wèi)?yīng)該有著嚴(yán)格的Code Review過程,怎么會(huì)讓這樣的代碼正式發(fā)布?要知道一個(gè)接口一旦公開,就不可以刪除了。也就是說,微軟很難彌補(bǔ)這個(gè)錯(cuò)誤。
如果是方法名不好,或者職責(zé)有些不明確,這樣還可以在舊方法上添加ObsoleteAttribute(這樣編譯器便會(huì)提示用戶這個(gè)方法已經(jīng)過期),并且將舊方法的調(diào)用委托給新的實(shí)現(xiàn)。例如:
- 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();
- ...
- }
可是,現(xiàn)在的問題是一個(gè)“類”,而這個(gè)類已經(jīng)無處不在了,例如在RouteData中有一個(gè)屬性Route,它便是RouteBase類型——如果將其修改為IRoute接口,那么至少也需要項(xiàng)目重新編譯之后才能夠“升級(jí)”。而作為一個(gè)公開類庫(kù),尤其是.NET這種成熟框架來說,應(yīng)該做到“無痛”才對(duì)。
本文來自趙劼的博客園文章《ASP.NET Routing中最令人摸不著頭腦的設(shè)計(jì)》
【編輯推薦】