Чтобы быть взаимозаменяемыми и тестируемыми, обычно сервисы с логикой должны иметь интерфейс, например
public class FooService: IFooService
{ ... }
Что касается дизайна, я согласен с этим, но одна вещь, которая беспокоит меня при таком подходе, заключается в том, что для одного сервиса вам нужно объявить две вещи (класс и интерфейс), а в нашей команде обычно два файла (один для класса и один для интерфейса). Еще одним дискомфортом является сложность навигации, поскольку использование «Перейти к определению» в IDE (VS2010) будет указывать на интерфейс (поскольку другие классы ссылаются на интерфейс), а не на фактический класс.
Я думал, что запись IFooService в тот же файл, что и FooService уменьшит вышеупомянутую странность. В конце концов, IFooService и FooService очень связаны. Это хорошая практика? Есть ли веская причина, по которой IFooService должен находиться в собственном файле?
источник
Ответы:
Это не должно быть в своем собственном файле, но ваша команда должна выбрать стандарт и придерживаться его.
Кроме того, вы правы, что «Перейти к определению» приводит вас к интерфейсу, но если у вас установлен Resharper , то только один клик откроет список производных классов / интерфейсов этого интерфейса, так что это не имеет большого значения. Вот почему я держу интерфейс в отдельном файле.
источник
Я думаю, вы должны хранить их в отдельных файлах. Как вы сказали, идея в том, чтобы оставаться тестируемым и взаимозаменяемым. Помещая интерфейс в тот же файл, что и ваша реализация, вы связываете интерфейс с конкретной реализацией. Если вы решите создать фиктивный объект или другую реализацию, интерфейс не будет логически отделен от FooService.
источник
Согласно SOLID, вы не только должны создавать интерфейс, и не только он должен находиться в другом файле, но и в другой сборке.
Зачем? Потому что любое изменение исходного файла, который компилируется в сборку, требует перекомпиляции сборки, а любое изменение сборки требует перекомпиляции любой зависимой сборки. Итак, если ваша цель, основанная на SOLID, состоит в том, чтобы иметь возможность заменить реализацию A реализацией B, в то время как класс C, зависящий от интерфейса, мне не нужно знать разницу, вы должны убедиться, что сборка с I в нем не меняется, тем самым защищая обычаи.
«Но это просто перекомпиляция», я слышу ваш протест. Ну, это может быть, но в вашем приложении для смартфона, которое облегчает пропускную способность данных ваших пользователей; загрузить один двоичный файл, который изменился, или загрузить этот двоичный файл и пять других с кодом, который зависит от него? Не каждая программа написана для использования на настольных компьютерах в локальной сети. Даже в том случае, когда пропускная способность и память дешевы, более мелкие выпуски исправлений могут иметь значение, поскольку их несложно распространить на всю локальную сеть через Active Directory или аналогичные уровни управления доменом; Ваши пользователи будут ждать только несколько секунд, пока он будет применен при следующем входе в систему, а не несколько минут, пока все будет переустановлено. Не говоря уже о том, что чем меньше сборок нужно перекомпилировать при создании проекта, тем быстрее он будет построен,
Теперь, отказ от ответственности: это не всегда возможно или возможно сделать. Самый простой способ сделать это - создать централизованный проект «интерфейсы». Это имеет свои недостатки; код становится менее пригодным для повторного использования, потому что на интерфейсный проект И на проект реализации нужно ссылаться в других приложениях, повторно использующих слой постоянства или другие ключевые компоненты вашего приложения. Вы можете преодолеть эту проблему, разделив интерфейсы на более тесно связанные сборки, но в вашем приложении будет больше проектов, что делает полную сборку очень болезненной. Ключ - баланс и поддержание слабосвязанной конструкции; обычно вы можете перемещать файлы по мере необходимости, поэтому, когда вы увидите, что классу потребуется много изменений или что регулярно потребуются новые реализации интерфейса (возможно, для взаимодействия с недавно поддерживаемыми версиями другого программного обеспечения,
источник
Почему наличие отдельных файлов вызывает дискомфорт? Для меня это намного аккуратнее и чище. Обычно создается подпапка с именем «Interfaces» и помещается туда ваши файлы IFooServer.cs, если вы хотите видеть меньше файлов в обозревателе решений.
Причина, по которой интерфейс определяется в своем собственном файле, по той же причине, по которой классы обычно определяются в своем собственном файле: управление проектом проще, когда ваша логическая структура и структура файла идентичны, поэтому вы всегда знаете, какой файл определен для данного класса in. Это может упростить вашу жизнь при отладке (трассировки стека исключений обычно дают вам номера файлов и строк) или при слиянии исходного кода в репозитории контроля версий.
источник
Часто рекомендуется, чтобы файлы кода содержали только один класс или один интерфейс. Но эти методы кодирования являются средством для достижения цели - чтобы лучше структурировать ваш код, облегчая работу с ним. Если вам и вашей команде будет легче работать, если классы хранятся вместе с их интерфейсами, делайте это во что бы то ни стало.
Лично я предпочитаю иметь интерфейс и класс в одном и том же файле, когда есть только один класс, который реализует интерфейс, например, в вашем случае.
Что касается проблем с навигацией, я настоятельно рекомендую ReSharper . Он содержит несколько очень полезных ярлыков для перехода непосредственно к методу, реализующему определенный метод интерфейса.
источник
Во многих случаях вы хотите, чтобы ваш интерфейс был не только в отдельном файле для класса, но даже в отдельной сборке .
Например, интерфейс контракта на предоставление услуг WCF может совместно использоваться как клиентом, так и сервисом, если вы контролируете оба конца провода. Перемещая интерфейс в его собственную сборку, он будет иметь меньше собственных зависимостей сборки. Это значительно упрощает потребление клиентом, ослабляя связь с его реализацией.
источник
Редко, если вообще когда-либо имеет смысл иметь единственную реализацию интерфейса 1 . Если вы поместите открытый интерфейс и открытый класс, реализующий этот интерфейс, в один и тот же файл, велика вероятность того, что вам не нужен интерфейс.
Когда класс, который вы размещаете вместе с интерфейсом, является абстрактным , и вы знаете, что все реализации вашего интерфейса должны наследовать этот абстрактный класс, размещение двух в одном и том же файле имеет смысл. Вы все еще должны тщательно изучить свое решение об использовании интерфейса: спросите себя, подойдет ли сам абстрактный класс, и отбросьте интерфейс, если ответ утвердительный.
Однако, как правило, вы должны придерживаться стратегии «один общедоступный класс / интерфейс соответствует одному файлу»: за ней легко следовать, и она облегчает навигацию по вашему исходному дереву.
1 Примечательным исключением является случай, когда вам нужен интерфейс для тестирования, потому что ваш выбор среды моделирования накладывал это дополнительное требование на ваш код.
источник
sealed
. Если вы подозреваете, что в будущем вы сможете добавить второй подкласс, вы сразу же включите интерфейс. PS Достаточно качественный помощник по рефакторингу может быть вашим по скромной цене одной лицензии ReSharper :)virtual
.Интерфейсы принадлежат их клиентам, а не их реализациям, как определено в гибких принципах, практиках и шаблонах Роберта К. Мартина. Таким образом, объединение интерфейса и реализации в одном месте противоречит его принципу.
Код клиента зависит от интерфейса. Это дает возможность компиляции и развертывания клиентского кода и интерфейса без реализации. Чем у вас могут быть разные реализации, работающие как плагины для клиентского кода.
ОБНОВЛЕНИЕ: Это не гибкий принцип здесь. «Бригада четырех в шаблонах проектирования» еще в 1994 году уже говорила о клиентах, придерживающихся интерфейсов и программирующих с интерфейсами. Их мнение схоже.
источник
Мне лично не нравится "я" перед интерфейсом. Как пользователь такого типа, я бы не хотел видеть, что это интерфейс или нет. Тип это интересная вещь. С точки зрения зависимостей ваш FooService является ОДНОЙ возможной реализацией IFooService. Интерфейс должен находиться рядом с местом, где он используется. С другой стороны, реализация должна быть в том месте, где ее можно легко изменить, не влияя на клиента. Итак, я бы предпочел два файла - нормально.
источник
new
его, но, с другой стороны, я могу использовать его совместно или наоборот. ИI
это установленное соглашение об именах в .Net, так что это хорошая причина придерживаться его, хотя бы для согласованности с другими типами.I
в интерфейсе только введение в мои рассуждения о разделении в двух файлах. Код лучше читается и делает инверсию зависимостей более понятной.I
в вашей среде является нормальным: используйте его! (Но мне это не нравится :))Кодирование интерфейсов может быть плохим, на самом деле рассматривается как антишаблон для определенных контекстов и не является законом. Обычно хорошим примером кодирования интерфейсов анти-паттерна является случай, когда у вас есть интерфейс, который имеет только одну реализацию в вашем приложении. Другой вариант, если файл интерфейса становится настолько утомительным, что вам нужно скрыть его объявление в файле реализованного класса.
источник
Помещение класса и интерфейса в один и тот же файл не накладывает ограничений на то, как их можно использовать. Интерфейс все еще можно использовать для макетов и т. Д. Таким же образом, даже если он находится в том же файле, что и класс, который его реализует. Этот вопрос может быть воспринят исключительно как организационное удобство, и в этом случае я бы сказал, делай все, что делает твою жизнь проще!
Конечно, можно утверждать, что наличие двух в одном файле может привести к тому, что неосторожные будут считать их более программно связанными, чем они есть на самом деле, что является риском.
Лично, если бы я наткнулся на кодовую базу, которая использовала одно соглашение над другим, я бы не был слишком обеспокоен в любом случае.
источник