Подтвердить что ты не робот

Каковы преимущества использования интерфейса в С#?

Я несколько лет назад был вовлечен в проект по разработке программного обеспечения и был вынужден быстро изучить С#. Мой фон программирования слаб (классический ASP).

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

В частности, интерфейс. Я понимаю основы, но при написании приложения мне сложно найти практическое применение. Зачем нужно писать интерфейс для своего приложения?

Спасибо Кевин

4b9b3361

Ответ 1

В интерфейсе говорится, как что-то должно работать. Подумайте об этом как о контракте или шаблоне. Это ключ к таким вещам, как Inverson of Control или Dependancy Injection.

Я использую Structure Map как мой контейнер IoC. Это позволяет мне определить интерфейс для всех моих классов. Где вы могли бы сказать

Widget w = new Widget();

Я бы сказал

IWidget w = ObjectFactory.GetInstance<IWidget>();

Это очень мощно, потому что мой код не обязательно говорит о том, что такое Widget. Он просто знает, что Widget может сделать на основе интерфейса IWidget.

У этого есть большая сила для этого в том, что теперь, когда я использую контейнер IoC, я могу сделать еще пару отличных вещей. В моих модульных тестах, где мне нужно использовать виджет, я могу создать макет для виджета. Так что скажем, что мой виджет делает что-то очень мощное благодаря подключению к базе данных или веб-сервису, мой макет может имитировать подключение к этим ресурсам и возвращать мне зашитые данные. Это ускоряет мой тест и ведет себя более надежно. Поскольку я использую StructureMap, я могу сказать StructureMap загрузить реальную реализацию моего виджета во время производственного использования моего кода и издеваемую версию виджета во время тестирования либо программно, либо путем конфигурации.

Кроме того, поскольку я использую контейнер IoC, я могу предоставить интересные приложения для приложения, например, написать три разных способа кэширования данных. Я могу использовать кеш локального ящика для разработчиков, используя такой инструмент, как Lucene.NET для локального кеша. Я могу иметь сервер разработки, использующий кеш .NET, который отлично работает на одном поле. И тогда у меня может быть третий вариант для моих производственных серверов, использующих уровень кэша, такой как MemCache Win32 или Velocity. Пока все три реализации кэширования соответствуют одному и тому же интерфейсу, их фактическая реализация не касается меня (или моего кода) вообще. Я просто прошу StructureMap перейти к реализации текущей среды, а затем перейти к работе.

Если вы все-таки следуете за вложением Dependency Injection, тогда сюда пригодится интерфейс с контейнером IoC, таким как StructureMap, в котором я могу объявить использование класса с помощью интерфейса в конструкторе моего класса.

public class Widget(IWidgetRepository repository, IWidgetService service) : IWidget
{
    //do something here using my repository and service
}

И затем, когда я обновляю экземпляр Widget с помощью StructureMap, такого как

IWidget widget = ObjectFactory.GetInstance<IWidget>();

Обратите внимание, что я не указываю репозиторий или службу в конструкторе. StructureMap знает с помощью интерфейсов, указанных в конструкторе, как пойти, чтобы получить соответствующие экземпляры и передать их тоже. Это делает очень мощный и чистый код!

Все из простого определения интерфейсов и их умного использования!

Ответ 2

Основной случай - это случай "IWriter".

Предположим, вы создаете класс, который может писать на консоль, и он имеет всевозможные полезные функции, такие как write() и peek().

Затем вы хотите написать класс, который может писать на принтер, поэтому вместо повторного использования нового класса вы используете интерфейс IWriter.

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

Ответ 3

Один простой ответ: Используйте интерфейсы для программирования против контракта, а не для реализации.

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

Ответ 4

Пример. Рассмотрите приложение MDI, которое показывает отчеты, там в основном 2 разных типа отчетов. Диаграмма и сетка. Мне нужно сохранить эти отчеты в формате PDF, и мне нужно отправить их кому-то. Обработчик событий для меню, которое пользователь нажимает, чтобы сохранить отчет в PDF, может сделать это:

void ExportPDF_Clicked(...) {
   if(currentDocument is ChartReport) {
      ChartReport r = currentDocument as ChartReport;
      r.SavePDF();
   } else if(currentDocument is GridReport) {
     GridReport r = currentDocument as GridReport;
      r.SavePDF();
   }
}

Я скорее сделаю свой ChartReport и GridReport реализовать этот интерфейс:

public interface Report {
  void MailTo();
  void SavePDF();
}

Теперь я могу сделать:

void ExportPDF_Clicked(...) {
   Report r = currentDocument as Report;
   r.SavePDF();
}

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

Ответ 5

IOC и инъекция зависимостей уже были упомянуты выше, и я хотел бы предложить вам посмотреть на них.

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

Допустим, у меня есть класс Foo, который имеет функции x и y и свойство z, и я создаю свой код вокруг него.

Если я найду лучший способ сделать Foo, или другой вид Foo требует реализации, я могу, конечно, расширить базовый класс Foo до FooA, FooB, MyFoo и т.д., однако это потребует, чтобы все Foos имели одинаковые или, действительно, любые будущие создатели Foo имеют доступ к базовому классу Foo и понимают его внутреннюю работу. В С# это означает, что будущий Foos не может наследовать ни от чего другого, кроме Foo, поскольку С# не поддерживает множественное наследование.

Это также потребовало бы, чтобы я знал о возможных будущих состояниях Foo и старался не мешать им в моем базовом классе Foo.

Используя интерфейс IFoo, просто указывается "контракт", который класс должен работать в моей инфраструктуре Foo, и мне все равно, какие будущие классы Foo могут наследовать или выглядеть внутренне, если они имеют fn x fn y и z. Это делает структуру более гибкой и открытой для будущих дополнений.

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

Ответ 6

хорошая статья.

Интерфейс - это контракт, который гарантирует клиенту, как будет себя вести класс или структура.

http://www.codeguru.com/csharp/csharp/cs_syntax/interfaces/article.php/c7563

Это может быть самый простой способ объяснить, что я столкнулся:

"Ответ заключается в том, что они обеспечивают довольно безопасное для типа средство для создания подпрограмм, которые принимают объекты, когда вы не знаете конкретного типа объекта, который будет передаваться раньше времени. Единственное, что вы знаете об объектах, которые будут переданы вашей подпрограмме в том, что у них есть определенные члены, которые должны присутствовать для вашей подпрограммы, чтобы иметь возможность работать с этим объектом. Лучший пример, который я могу назвать необходимостью интерфейсов, - это командная среда. Интерфейсы помогают определить, как разные компоненты разговаривают друг с другом. Используя интерфейс, вы исключаете возможность того, что разработчик неправильно интерпретирует, какие члены они должны добавить к типу или как они будут вызывать другой тип, определяющий интерфейс. Без интерфейса ошибки ползут в систему и не отображаются до времени выполнения, когда их трудно найти. С интерфейсами ошибки в определении типа обнаруживаются сразу во время компиляции, где стоимость намного меньше.

Ответ 7

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

Ответ 8

Пара вещей, когда вы наследуете интерфейс, заставляет вас реализовать все методы, определенные в интерфейсе. Для другого это также хороший способ принести множественное наследование, которое не поддерживается для обычных классов. http://msdn.microsoft.com/en-us/library/ms173156.aspx

Ответ 9

Простой ответ, основанный на первых принципах:

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

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

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

Ответ 10

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

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

public interface IServices
{
    DataManager Data { get; set; }
    LogManager Log { get; set; }
    SomeOtherManager SomeOther { get; set; }
}

public class MrPlugin
{
    public void Init(IServices services)
    {
        // Do stuff with services
    }
}

Итак.. Если у вас есть класс, который реализует интерфейс IServices, а затем вы его создаете один раз, вы можете передать его всем плагинам при инициализации, и они могут использовать любые сервисы, определенные вами в интерфейсе.