Интерфейс маркера, реализующий два или более других интерфейса - PullRequest
3 голосов
/ 20 октября 2010

Считается ли хорошей или плохой практикой написание интерфейса, который существует исключительно для концентрации других интерфейсов?

interface InterfaceA : InterfaceB, InterfaceC {
}

При этом для конкретной реализации требуется только реализация InterfaceA, что кажется хорошей вещью, но не добавляет никакой ценности расширению, что кажется расточительным.

Ответы [ 4 ]

2 голосов
/ 20 октября 2010

Я думаю, это зависит от того, имеет ли это смысл в вашем домене.Если это произойдет, я бы сделал это, тогда ваша программа более точно моделирует ваш домен.

Кроме того, конечно, теперь вы можете написать код, который требует / использует объекты, реализующие InterfaceA, т.е. все методы обоихсупер-интерфейсы;это было бы невозможно раньше.

1 голос
/ 08 марта 2012

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

Во-первых, мы должны знать, что такой пустой интерфейс также известен какинтерфейс маркера - этот другой, оставшийся без ответа, SO обсуждает проблемы интерфейсов маркера , и ответ Скотта Вишневси (в настоящее время верхний) интересен при рассмотрении остальной части того, что я собираюсь сказать.

Висследуя пустые интерфейсы, вы, как правило, наткнетесь на тему MSDN, касающуюся предупреждения анализа кода CA1040 , в которой, в частности, предлагается избегать пустых интерфейсов, которые не реализуют по крайней мере двух других.

По этому признаку - и, конечно, это не обязательно означает, что руководящие принципы Microsoft по кодированию де-факто - в вашем примере это именно то, что вы делаете (и то, что я хочу сделать также в моем текущем проекте), и поэтому это будетпройти собственный лакмусовой тест MS.В результате я с радостью скажу: «Да, такой интерфейс вполне разумен».

Для обоснования, и я думаю, что особенно хороший пример причины, по которой установлен переключатель «по крайней мере два интерфейса»Это правило похоже на ситуацию, в которой я нахожусь:

public interface IReadsAResource
{
  public byte[] Read(string id);
}

public interface IWritesAResource
{
  //returns the id
  public string Write(byte[] resource);
}

Учитывая эти два интерфейса, теперь я могу написать компонент, который прямо заявляет, что ему нужно только читать или писать:

public class NeedsRead
{
  private readonly IReadsAResource Reader;
  public NeedsRead(IReadsAResource reader){ Reader = reader; }
}

public class NeedsWrite
{
  private readonly IWritesAResource Writer;
  public NeedsWrite(IWritesAResource writer){ Writer = writer; }
}

Теперь я обычно выбираю реализацию этого интерфейса для одного класса ResourceReaderWriter, учитывая, что для хранилища данных для ресурсов потребуются зависимости, которые являются общими для обеих реализаций метода, передавая его, когда необходим любой интерфейс:

var needsRead = new NeedsRead(new ResourceReaderWriter(/* dependencies */));
var needsWrite = new NeedsWriter(new ResourceReaderWriter(/* dependencies */));

Но что тогда, если у меня есть класс, который должен одновременно читать и записи ресурсов?Сейчас, когда экосистема интерфейса уже готова, мне нужны два параметра конструктора:

public class NeedsReadAndWrite{
  public NeedsReadAndWrite(IReadsAResource reader, IWritesAResource writer){
    /* reader/writer local variables elided */
  }
}

Вы можете сказать, что это не такая уж сложная задача, но она вызывает две проблемы:

  • Выводчто у нас есть два отдельных экземпляра, которые отвечают за чтение и запись ресурсов;поэтому наш класс не может быть уверен, что оба соответствуют друг другу.Точно так же нельзя быть уверенным, что между двумя состояниями правильно управляется.
  • Как вызывающая сторона, я, конечно, могу обойти это, сначала создав один экземпляр и передав его обоим параметрам.Но это лишает меня возможности создавать объект в одну строку, и кажется странным .

Любой, кто застрял со мной так долго, увидит, куда я идуnow:)

Элегантное решение состоит в том, чтобы ввести интерфейс для типа, который поддерживает чтение и запись:

//denotes a component that can both read and write
public interface IReadsAndWritesAResource : IReadsAResource, IWritesAResource
{

}

С этим интерфейсом наш проблемный тип можетпереписать как

public class NeedsReadAndWrite{
  public NeedsReadAndWrite(IReadsAndWritesAResource readerWriter){
    /* local variable assignment elided */
  }
}

И обе эти проблемы теперь обойдены стороной.

В равной степени становится просто создать базовый тип прокси, который реализует этот новый интерфейс, используя примерычитатель и писатель и перенаправляют вызовы чтения / записи к ним.

Я думаю, что этот пример демонстрирует, почему вы, или, возможно, даже должны , использовать интерфейсы маркеров, которые объединяют дваили больше других.

1 голос
/ 20 октября 2010

Это действительно полезно для поддержания читабельности кода, тогда вы можете использовать конкретную реализацию интерфейса A, где требуется интерфейс B или C.

1 голос
/ 20 октября 2010

Если это имеет смысл, и вы собираетесь использовать все три интерфейса независимо, это, безусловно, может быть подходящим решением для проектирования.

Если окажется, что InterfaceB и IntefaceC никогда не будут использоваться, просто сделайте InterfaceA ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...