如何写出漂亮的代码——巧妙的接口扩展
最近对面向对象有了个新的领悟,特在此分享给大家。如果这个思想不对或者已经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,其实可以应用于任何地方。
思想的升华:
本人觉得使用“接口扩展”的设计要比用基类的设计要好些,因为“接口扩展”像是一种注入式的代码,我们可以将接口封装的特性注入到任何一个类上面,而不用去处理复杂的类的继承。
给你的实体类注入新特性吧!