接口返回派生类型

时间:2012-02-27 03:46:54

标签: c# design-patterns repository

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

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

我有一个相当通用的界面,可以搜索“某事”并返回ResultIRepository接口可以由多个类实现,每个类都使用自己的唯一元数据返回自己的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特定结果的额外信息。如果我创建了另一个实现IRepository的类,那么该特定实现可能有自己的元数据集对该类唯一。

最后,我希望我的搜索控制器看起来像这样:

class SearchController 
{
   private IRepository[] _repositories;

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

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

我可以轻松地在Data类上显示Result属性,但是有一个很好的模式来显示从Result派生的每个类的元数据吗?我可以有一堆if语句来检查类是否是一个类型,但感觉有点笨拙。有没有更好的方法来做我想要实现的目标?

4 个答案:

答案 0 :(得分:1)

其中一条评论正确地指出,向Result类添加虚拟Display()违反了单一责任原则。完全正确。

以下是你的问题:因为你想做这样的事情:

private IRepository[] _repositories;

...没有办法避免在运行时进行类型检查。编译器不知道将返回哪个子类type-derived-from-Result。所有它知道你的Repository返回一个Result派生对象。

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

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

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

如果你坚持使用泛型,那么你可以做这样的事情:

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

所以,我想我的问题是:是否必须存储这些存储库的数组?现实世界的使用场景是什么?

答案 1 :(得分:1)

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

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实现,它通过元数据并正确地构建它。

答案 2 :(得分:0)

您可以将IRepository设为通用界面,如:

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

答案 3 :(得分:0)

您可以在结果类中使用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();
   }

}

希望这会对你有所帮助。