Интерфейс, возвращающий производные типы - PullRequest
4 голосов
/ 27 февраля 2012
class Result
{
   public string Data { get; set; }
}

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

У меня довольно общий интерфейс, который ищет «что-то» и возвращает Result. Интерфейс IRepository может быть реализован несколькими классами, каждый из которых возвращает свой собственный Результат со своими уникальными метаданными. Например, у меня может быть 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 ]

1 голос
/ 27 февраля 2012

Я бы не использовал интерфейс хранилища для этого, но создал бы новый:

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, которая проходит черезметаданные и структурируйте их должным образом.

1 голос
/ 27 февраля 2012

В одном из комментариев правильно указано, что добавление виртуального Display () в ваш класс Result является нарушением принципа единой ответственности.Совершенно верно.

Вот в чем проблема с вашим вопросом: потому что вы хотите сделать что-то вроде этого:

private IRepository[] _repositories;

... нет способа избежать проверки типа во время выполнения.Компилятор понятия не имеет, какой подкласс типа-производный-от-результата будет возвращен.Все, что он знает, это то, что ваш репозиторий возвращает объект, полученный в результате.

С другой стороны, если вы используете обобщенные значения:

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

... вы узнаете во время компиляциис каким типом подкласса Result вы имеете дело, тем самым устраняя необходимость проверки типов и длинной строки операторов «if», которые следуют из первого подхода.

Если вы можете придерживаться использования обобщений, тогдаВы можете сделать что-то вроде этого:

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

Итак, я полагаю, мой вопрос: обязательно ли хранить массив этих репозиториев?Какой сценарий использования в реальном мире существует?

0 голосов
/ 27 февраля 2012

Вы можете иметь метод 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();
   }

}

Надеюсь, это поможет вам.

0 голосов
/ 27 февраля 2012

Вы можете сделать IRepository универсальный интерфейс, например:

interface IRepository<T>
{
    T[] Search(string data);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...