最近對面向對象有了個新的領悟,特在此分享給大家。如果這個思想不對或者已經out了,還請不要笑話。
本文的示例代碼將以ASP.NET MVC為基礎的,如果你沒有MVC的基礎,也不會影響閱讀,因為本文探討的核心是面向對象中的一個設計思想。
下面我先以一個簡單的例子來描述它。
案例:
有時我們的一個項目包含多個網站,比如有一個管理員用的站點,有一個公司客戶使用的站點,這2個站點會部署到不同的服務器。這2個站點都有一個注銷登錄的action。你可能首先想到,用一個UserControllerBase類來封裝這個logout action,以實現代碼的重用。這當然是我們首先考慮到的。但是,有時候這2個站點的controllers分別繼承了不同的controllerBase,比如AdminControllerBase和ClientControllerBase,這時再用類的繼承來實現就不太優雅了。
解決方案:
定義一個ILogoutController接口,然后對該接口寫擴展方法Logout,然后再讓不同站點的UserController實現這個接口。
代碼:接口定義和擴展實現
namespace Demo { public interface ILogoutController { } public static class LogoutControllerExtensions { public static ActionResult Logout<TController>(this TController controller) where TController : DemoControllerBase, ILogoutController { var service = IoC.GetService<IUserService>(); service.Signout(controller.User.Identity.Name); controller.Session.Clear(); controller.Session.Abandon(); FormsAuthentication.SignOut(); return controller.DemoRedirect("/login"); } } }
代碼:讓相應的controller實現該接口
namespace Demo.Admins { public class UserController : AdminControllerBase, ILogoutController { public ActionResult Logoff() { return this.Logout(); } } } namespace Demo.Clients { public class UserController : ClientControllerBase, ILogoutController { public ActionResult Logoff() { return this.Logout(); } } }
上面代碼的核心在於接口擴展方法的泛型約束 where TController : DemoControllerBase, ILogoutController。另外還請注意到擴展方法的最后一行代碼:
return controller.DemoRedirect("/login");
由於System.Web.Mvc.Controller的Redirect方法是protected,為了讓該方法公開出來,在這個例子中我們新加了一個DemoControllerBase,如下:
public class DemoControllerBase : Controller { public RedirectResult DemoRedirect(string url) { return base.Redirect(url); } } public class AdminControllerBase : DemoControllerBase { } public class ClientControllerBase : DemoControllerBase { }
其他適用場景:
可以說這一設計思想可以應用到任何場景,可以說這一思想即是面向對象編程思想的一部分。你可以發現,上面的代碼是完全符合面向對象思想的。代碼非常容易閱讀,可維護性高,可擴展性強。
在我們的項目中,除了上面的ILogoutController外,還有一個IAddressEditorController。由於我們的項目分為4個站點,有很多的用戶角色,地址編輯出現在很多地方。自從有了這個接口之后,代碼就漂亮多了,如果客戶想要增加一個地址編輯頁面,對我們的成本的增加那就幾乎是0.
“接口擴展”這一思想不僅僅是可以應用於web層的controller,其實可以應用於任何地方。
思想的升華:
本人覺得使用“接口擴展”的設計要比用基類的設計要好些,因為“接口擴展”像是一種注入式的代碼,我們可以將接口封裝的特性注入到任何一個類上面,而不用去處理復雜的類的繼承。
給你的實體類注入新特性吧!