接口继承场景下重复实现同名方法的优化方案咨询
分页接口扩展的问题与优化方案
咱们先梳理下现有接口、实现以及遇到的痛点:
现有代码情况
原基础接口
public interface IPageParameter { int Pagenumber { get; set; } } public interface IPageModel { int CurrentPage { get; set; } IPageParameter GetLinkParameter(); }
扩展后的新接口
public interface IPageParameterExt : IPageParameter { int PerPage { get; set; } } public interface IPageModelExt : IPageModel { int CurrentPerPage { get; set; } new IPageParameterExt GetLinkParameter(); }
当前实现类
public class DefaultPageParameterExt : IPageParameterExt { public int Pagenumber { get; set; } public int PerPage { get; set; } } public class PageModel : IPageModelExt { public int CurrentPage { get; set; } public int CurrentPerPage { get; set; } IPageParameterExt IPageModelExt.GetLinkParameter() { return new DefaultPageParameterExt() { Pagenumber = CurrentPage, PerPage = CurrentPerPage }; } IPageParameter IPageModel.GetLinkParameter() { return ((IPageModelExt)this).GetLinkParameter(); } }
关联的IHtmlHelper扩展
public static IHtmlContent GetPageButtons<TModel>(this IHtmlHelper<TModel> helper, TModel Model) where TModel : IPageModel { var urlHelper = new UrlHelper(helper.ViewContext); var html = new HtmlContentBuilder(); var para = Model.GetLinkParameter(); string prevclass = Model.CurrentPage <= 1 ? "btn btn-default disabled" : "btn btn-default"; html.AppendHtml(@"<table style=""width:1%"">") .AppendHtml("<tr>") .AppendHtml("<td>"); /*other buttons*/ para.Pagenumber = Model.CurrentPage -1; html.AppendFormat(@"<a href=""{1}"" class=""{0}"">{2}</a>", prevclass, urlHelper.Action("", para), "<") .AppendHtml("</td>") /*other buttons*/ .AppendHtml("</tr>") .AppendHtml("</table>"); return html; }
痛点分析
你提到的问题很准确:当前实现每次扩展接口都要新增GetLinkParameter的显式实现,后续如果还要加更多分页参数(比如排序、筛选),岂不是要写一堆重复的方法?能不能让子类接口复用父接口的实现,或者用同一个方法搞定两个接口的要求?
优化方案
方案1:优化现有显式实现的复用逻辑
其实你当前的实现已经在复用了——父接口的GetLinkParameter直接调用子接口的实现。后续如果再扩展,比如新增IPageModelExt2 : IPageModelExt,只需要在实现类里显式实现最上层的GetLinkParameter(),下层接口的方法直接复用即可:
// 新增扩展接口 public interface IPageModelExt2 : IPageModelExt { string SortField { get; set; } new IPageParameterExt2 GetLinkParameter(); } // 实现类里只需要新增最上层的显式实现 public class PageModel : IPageModelExt2 { // ... 原有属性 ... public string SortField { get; set; } IPageParameterExt2 IPageModelExt2.GetLinkParameter() { return new DefaultPageParameterExt2() { Pagenumber = CurrentPage, PerPage = CurrentPerPage, SortField = SortField }; } // 中间层接口的方法直接调用上层实现 IPageParameterExt IPageModelExt.GetLinkParameter() { return ((IPageModelExt2)this).GetLinkParameter(); } // 父接口方法依然复用 IPageParameter IPageModel.GetLinkParameter() { return ((IPageModelExt)this).GetLinkParameter(); } }
这种方式不用改原有接口,每次扩展时只需要实现最上层的方法,下层的都通过向上转型复用,大幅减少重复代码。
方案2:用泛型协变重构接口(更优雅的长期方案)
如果可以修改原接口,推荐用泛型协变的方式,从根源上避免重复实现:
重构后的接口定义
// 原基础接口不变 public interface IPageParameter { int Pagenumber { get; set; } } // 把IPageModel改成泛型接口,支持协变 public interface IPageModel<out TParam> where TParam : IPageParameter { int CurrentPage { get; set; } TParam GetLinkParameter(); } // 扩展接口直接继承泛型父接口 public interface IPageParameterExt : IPageParameter { int PerPage { get; set; } } public interface IPageModelExt : IPageModel<IPageParameterExt> { int CurrentPerPage { get; set; } // 不需要重新定义GetLinkParameter,直接继承自IPageModel<IPageParameterExt> }
实现类(只需要写一次GetLinkParameter)
public class DefaultPageParameterExt : IPageParameterExt { public int Pagenumber { get; set; } public int PerPage { get; set; } } public class PageModel : IPageModelExt { public int CurrentPage { get; set; } public int CurrentPerPage { get; set; } // 只需要实现这一个方法,同时满足所有父接口的要求 public IPageParameterExt GetLinkParameter() { return new DefaultPageParameterExt() { Pagenumber = CurrentPage, PerPage = CurrentPerPage }; } }
适配IHtmlHelper扩展
修改扩展方法的泛型约束,同时保留兼容原有调用的重载:
// 通用泛型版本,支持所有IPageModel<TParam> public static IHtmlContent GetPageButtons<TModel, TParam>(this IHtmlHelper<TModel> helper, TModel Model) where TModel : IPageModel<TParam> where TParam : IPageParameter { var urlHelper = new UrlHelper(helper.ViewContext); var html = new HtmlContentBuilder(); var para = Model.GetLinkParameter(); string prevclass = Model.CurrentPage <= 1 ? "btn btn-default disabled" : "btn btn-default"; html.AppendHtml(@"<table style=""width:1%"">") .AppendHtml("<tr>") .AppendHtml("<td>"); /*other buttons*/ para.Pagenumber = Model.CurrentPage -1; html.AppendFormat(@"<a href=""{1}"" class=""{0}"">{2}</a>", prevclass, urlHelper.Action("", para), "<") .AppendHtml("</td>") /*other buttons*/ .AppendHtml("</tr>") .AppendHtml("</table>"); return html; } // 兼容原有代码的重载,不用改原有调用逻辑 public static IHtmlContent GetPageButtons<TModel>(this IHtmlHelper<TModel> helper, TModel Model) where TModel : IPageModel<IPageParameter> { return GetPageButtons<TModel, IPageParameter>(helper, Model); }
这种方式的好处是,后续再扩展分页参数,只需要新增IPageParameter的子接口和对应的IPageModel子接口,实现类只需要实现一次GetLinkParameter,完全不用重复写多个接口的实现。
方案3:用抽象类做中间层(不修改原接口的另一种选择)
如果不能修改原接口,可以创建一个抽象类,把父接口的实现逻辑封装起来,子类只需要实现扩展部分:
public abstract class PageModelBase : IPageModel { public int CurrentPage { get; set; } // 父接口的方法,调用抽象方法由子类实现 IPageParameter IPageModel.GetLinkParameter() { return GetLinkParameterCore(); } // 抽象方法,子类实现扩展版本的参数获取 protected abstract IPageParameterExt GetLinkParameterCore(); } public class PageModel : PageModelBase, IPageModelExt { public int CurrentPerPage { get; set; } protected override IPageParameterExt GetLinkParameterCore() { return new DefaultPageParameterExt() { Pagenumber = CurrentPage, PerPage = CurrentPerPage }; } // 子接口的方法直接调用抽象方法 IPageParameterExt IPageModelExt.GetLinkParameter() { return GetLinkParameterCore(); } }
后续扩展时,只需要修改抽象类的抽象方法(如果需要),或者让子类新增对应的实现,父接口的逻辑始终由抽象类处理,不用重复写。
总结
- 如果可以修改原接口,泛型协变的重构方案是最优选择,能彻底避免重复实现,同时保证类型安全;
- 如果不能修改原接口,优化显式实现的复用逻辑或者用抽象类中间层,都能有效减少后续扩展的重复代码;
- 你当前的实现其实已经有复用的思路,只要后续扩展时遵循“上层接口方法调用更上层的实现”的逻辑,就不用每次新增多个显式实现。
内容的提问来源于stack exchange,提问作者Yobikir
相关产品推荐
相关产品推荐

