Использование явных интерфейсов для обеспечения программирования с интерфейсом - PullRequest
8 голосов
/ 07 октября 2008

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

Пример:

public interface ICut
{
    void Cut();
}
public class Knife : ICut
{
    void ICut.Cut()
    {
        //Cut Something
    }
}

А для использования объекта Нож:

ICut obj = new Knife();
obj.Cut();

Вы бы порекомендовали этот метод реализации интерфейса? Почему или почему нет?

EDIT: Кроме того, учитывая, что я использую явный интерфейс, следующее НЕ будет работать.

Knife obj = new Knife();
obj.Cut();

Ответы [ 11 ]

4 голосов
/ 07 октября 2008

Цитирую главу 1 GoF:

  • «Программа для интерфейса, а не для реализации».
  • "Сочетание объектов с наследованием классов".

Поскольку C # не имеет множественного наследования, составление объектов и программирование интерфейсов - это путь.

ETA: И вы никогда не должны использовать множественное наследование, но это совсем другая тема ..: -)

ETA2: Я не уверен в явном интерфейсе. Это не кажется конструктивным. Зачем мне хотеть иметь Нож, который может выполнять Cut () только в том случае, если он создан как ICut?

3 голосов
/ 07 октября 2008

Я использовал его только в тех случаях, когда я хочу ограничить доступ к определенным методам.

public interface IWriter
{
    void Write(string message);
}

public interface IReader
{
    string Read();
}

public class MessageLog : IReader, IWriter
{
      public string Read()
      {
           // Implementation

          return "";
      }

      void IWriter.Write(string message)
      {
           // Implementation
      }
}

public class Foo
{
    readonly MessageLog _messageLog;
    IWriter _messageWriter;

    public Foo()
    {
         _messageLog = new MessageLog();
         _messageWriter = _messageLog;
    }

    public IReader Messages
    {
        get { return _messageLog; }
    }
}

Теперь Foo может записывать сообщения в свой журнал сообщений, используя _messageWriter, но клиенты могут только читать. Это особенно полезно в сценарии, где ваши классы ComVisible. Ваш клиент не может привести к типу Writer и изменить информацию в журнале сообщений.

2 голосов
/ 26 октября 2008

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

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

2 голосов
/ 24 октября 2008

Это плохая идея, потому что их использование нарушает полиморфизм. Тип используемой ссылки НЕ должен изменять поведение объекта. Если вы хотите обеспечить слабую связь, сделайте классы внутренними и используйте технологию DI (такую ​​как Spring.Net).

2 голосов
/ 07 октября 2008

Да. И не только для тестирования. Имеет смысл выделить обычное поведение в интерфейс (или абстрактный класс); таким образом, вы можете использовать полиморфизм.

public class Sword: ICut
{    
  void ICut.Cut()   
  {        
     //Cut Something   
  }
}

Фабрика может вернуть тип острого орудия!:

ICut obj = SharpImplementFactory();

obj.Cut();
1 голос
/ 12 июня 2009

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

public class Knife : ICut
{
     public void Slice()
     {
          // slice something
     }

     void ICut.Cut()
     {
          Slice();
     }
}
1 голос
/ 24 октября 2008

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

Рассмотрим логическую сущность в игре, и вы решаете, что вместо класса, отвечающего за рисование / пометку объектов, вы хотите, чтобы код для отметки / рисования был в сущности.

Реализация IDrawable.draw () и ITickable.tick () гарантирует, что сущность может быть нарисована / помечена, только когда игра этого ожидает. В противном случае эти методы никогда не будут видны.

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

1 голос
/ 07 октября 2008

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

Однако, если вы комбинируете интерфейсное программирование с Inversion of Control и Dependency Injection, то вы действительно чего-то добиваетесь. Вы также можете использовать Mock Objects для модульного тестирования с этим типом установки, если вы участвуете в разработке через тестирование.

Однако IOC, DI и TDD сами по себе являются основными темами, и по каждому из этих предметов написаны целые книги. Надеюсь, это даст вам отправную точку для изучения вещей.

1 голос
/ 07 октября 2008

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

Кроме того, это помогает упростить наследование и дает больше гибкости в использовании полиморфизма.

0 голосов
/ 07 октября 2008

Да, но не обязательно по указанным причинам.

Пример:

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

На этой странице есть навигация и кнопки для взаимодействия со всеми общими действиями.

Определив интерфейс (IDataEntry), который реализует методы для каждой из функций, и реализовав этот интерфейс для каждого из элементов управления, мы можем использовать открытые методы aspx page для пользовательских элементов управления, которые выполняют фактический ввод данных. 1009 *

Определяя строгий набор методов взаимодействия (например, ваш метод 'cut' в примере), интерфейсы позволяют вам взять объект (будь то бизнес-объект, веб-элемент управления или что-то еще) и работать с ним определенным образом.

Например, вы можете назвать вырезание на любом объекте ICut, будь то нож, пила, паяльная лампа или монофиламентная нить.

Для тестирования я думаю, что интерфейсы тоже хороши. Если вы определяете тесты на основе ожидаемой функциональности интерфейса, вы можете определять объекты, как описано, и тестировать их. Это тест очень высокого уровня, но он по-прежнему обеспечивает функциональность. ОДНАКО, это не должно заменять модульное тестирование отдельных методов объекта ... не стоит знать, что 'obj.Cut' приводил к обрезке, если он приводил к обрезке неправильной вещи или в неправильном месте. *

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