繁体   English   中英

返回派生类型的接口

[英]Interface returning derived types

class Result
{
   public string Data { get; set; }
}

interface IRepository
{
   Result[] Search(string data);
}

我有一个相当通用的接口,它搜索“某物”并返回Result IRepository接口可以由几个类实现,每个类都返回带有自己唯一的元数据的自己的Result。 例如,我可以拥有一个DiskRepository来搜索磁盘上的数据:

class DiskResult : Result
{
   public int FileSize { get; set; }
   public DateTime LastModifiedDate { get; set; }
}

class DiskRepository : IRepository
{
   public Result[] Search(string data) 
   {
      // ...
      DiskResult[] results = GetDataFromSomewhere();
      return results;
   } 
}

DiskResult包含有关特定于DiskRespository的结果的DiskRespository 如果我创建了另一个实现IRepository类,则该特定实现可能具有该类独特的元数据集。

最后,我希望搜索控制器如下所示:

class SearchController 
{
   private IRepository[] _repositories;

   public SearchController(IRepository[] repositories)
   {
      _repositories = repositories;
   }

   public void Display(string data)
   {
      Result[] results = _repositories.Search(data);
      // Display results
   }
}

我可以轻松地在Result类上显示Data属性,但是是否有一种很好的模式来显示从Result派生的每个类的元数据? 我可以有一堆if语句来检查类是否为类型,但这感觉有些笨拙。 有没有更好的方法来完成我要达到的目标?

其中一条评论正确指出,将虚拟Display()添加到Result类中是违反单一职责原则的。 完全正确。

这是您的问题的答案:因为您想做这样的事情:

private IRepository[] _repositories;

...无法避免在运行时进行类型检查。 编译器不知道将返回哪个子类type-from-from-Result。 所有它知道您的存储库都返回一个Result派生对象。

另一方面,如果您使用泛型:

interface IRepository<T> where T : Result
{
    T[] Search(string data);
}

...您将在编译时知道您要处理的Result的子类类型,从而避免了类型检查的需要,以及从第一种方法开始的长串“ if”语句。

如果您可以坚持使用泛型,那么可以执行以下操作:

interface IResultDisplayService<T> where T : Result
{
    void Display(T result);
}

因此,我想我的问题是:是否必须存储这些存储库的数组? 有哪些现实世界的使用场景?

我不会为此使用存储库接口,而是创建一个新接口:

public interface ISearchProvider
{
    IEnumerable<SearchResultItem> Search(string keyword);
}

public interface ISearchResultItem
{
    string Title {get; }
    string Description {get; }
    NameValueCollection Metadata {get; }
}

标题和描述应足以满足90%的搜索用例。 例如, DiskResult可能在Description属性中包含文件夹等。 元数据可以显示在工具提示或详细信息视图中。

如果那还不够,我也会创建一个渲染界面:

public interface ISearchResultRenderer
{
    bool IsValidFor(Type type);
    void Render(Stream stream);
}

并具有一个DiskResultHtmlRenderer实现,该实现将遍历元数据并正确地对其进行结构化。

您可以将IRepository通用接口,例如:

interface IRepository<T>
{
    T[] Search(string data);
}

您可以在结果类中有一个virtual方法来显示结果。 您的子类可以override它,并可以给出自己的实现。 这样做时,当您调用Display方法时,您的Result对象将调用相应的方法进行显示。

像这样

class Result
{
     public virtual void Display()
     {
          //Your Code

     }
     //Your Code   
}

class DiskResult : Result
{
     public override void Display()
     {
          //Your Code
     }
     //Your Code
}

您的Display方式

public void Display(string data)
{
   Result[] results = _repositories.Search(data);

   // Display results
   foreach(var result in results)
   {
      result.Display();
   }

}

希望这对您有所帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM