2010-10-20 3 views
3

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

interface InterfaceA : InterfaceB, InterfaceC { 
} 

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

+0

И почему я внезапно появляется как «user123432»? Это не мой идентификатор на StackOverflow. – redman

+0

Есть ли [это] (http://stackoverflow.com/users/123432/user123432) ваши вопросы? Во всяком случае, вы можете посетить [meta.stackoverflow.com] (http://meta.stackoverflow.com), чтобы проверить наличие ошибок. – Boldewyn

+0

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

ответ

2

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

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

+0

Отмечено как принятый ответ, потому что, хотя все ответы были правильными, глядя на него из угла домена, он зафиксировал его. Это действительно имеет смысл в моей области, и ответ был бы очевиден, если бы я думал с этого момента. – redman

1

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

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

1

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

1

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

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

В исследовании пустых интерфейсов вы обычно натыкаетесь the MSDN topic concerning Code Analysis warning 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, учитывая, что хранилище данных для ресурсов будет req Зависимости uire, которые разделяют обе реализации метод, передавая экземпляр, что когда либо интерфейс необходимо:

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 */ 
    } 
} 

Не такой трудности, вы можете сказать, но это вызывает две проблемы:

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

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

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

//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 */ 
    } 
} 

И обе эти проблемы теперь являются побочными.

Аналогичным образом становится простым создание базового прокси-типа, реализующего этот новый интерфейс, путем ввода экземпляров считывателя и записи и пересылки вызовов чтения/записи на них.

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

+0

Я бы предположил, что пустой интерфейс, который наследует точно один другой, может быть хорошей и полезной вещью (хотя и недоиспользуемой). Например, я хотел бы увидеть несколько производных, если 'IEnumerable ', некоторые с добавленными членами и некоторые без. Например, 'IMultipassEnumerable ' должен быть получен из 'IEnumerable ' и возвратил 'IMultipassEnumerator ' с помощью метода 'Reset()', который не должен был находиться в 'IEnumerator '). 'IImmutableEnumerable ' должен был быть получен из 'IMultipassEnumerable ' и ... – supercat

+0

... не добавил никаких новых членов, а вместо этого добавил обещание, что все счетчики, возвращенные 'GetEnumerator', вернут тот же набор объектов' T' (обратите внимание, что сами объекты 'T' могут быть или не быть неизменными, обещание состояло бы в том, что перечислитель всегда возвращал один и тот же набор из них). Код, который будет хранить ссылку на 'IEnumerable ' для хранения содержимого, может использовать 'IImmutableEnumerable '. – supercat

+0

Да, я могу определенно увидеть потенциал там, и аргумент для него. Проблема с действительно пустым интерфейсом, который, предположительно, обещает что-то подобное, заключается в том, что нет никакого способа обеспечить его соблюдение. То, что класс реализует этот интерфейс, не гарантирует, что он будет играть честно. Но в закрытой системе это не проблема, я думаю. –

Смежные вопросы