Интерфейс безумие - PullRequest
       49

Интерфейс безумие

8 голосов
/ 31 марта 2009

Я пью клевету и ей очень нравится - интерфейсы, IoC, DI, TDD и т. Д. И т. Д. Работает довольно хорошо. Но я обнаружил, что должен бороться с тенденцией сделать все интерфейсом! У меня есть фабрика, которая является интерфейсом. Его методы возвращают объекты, которые могут быть интерфейсами (может упростить тестирование). Эти объекты представляют собой DI-интерфейсы к нужным им сервисам. Я обнаружил, что синхронизация интерфейсов с реализациями добавляет работы: добавление метода в класс означает добавление его в класс + интерфейс, макеты и т. Д.

Я делаю интерфейсы слишком рано? Существуют ли передовые практики, чтобы знать, когда что-то должно возвращать интерфейс против объекта?

Ответы [ 8 ]

7 голосов
/ 10 апреля 2009

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

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

Существует определенная конструктивная ценность при извлечении интерфейса из хранилища. Моя конкретная реализация репозитория вполне может быть тесно связана с NHibernate или ActiveRecord. Связывая мой сервис с интерфейсом, я получаю четкое разделение от этой детали реализации. Так уж получилось, что теперь я могу написать супербыстрые автономные модульные тесты для своего сервиса, теперь я могу передать его имитирующему IRepository.

Учитывая доменный объект, который вернулся из хранилища и на который действует моя служба, ценность меньше. Когда я пишу тест для своего сервиса, я хочу использовать реальный объект домена и проверять его состояние. Например. после вызова service.AddSomething () я хочу проверить, что что-то было добавлено к объекту домена. Я могу проверить это, просто проверив состояние объекта домена. Когда я тестирую свой доменный объект изолированно, мне не нужны интерфейсы, так как я собираюсь только выполнять операции над объектом и проверять его во внутреннем состоянии. например может ли овца есть траву, если она спит?

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

Помните, что (если у вас установлена ​​копия Resharper), чрезвычайно дешево извлечь интерфейс позже. Также дешево удалить интерфейс и вернуться к более простой иерархии классов, если вы решите, что вам все-таки не нужен этот интерфейс. Мой совет - начинать без интерфейсов и извлекать их по требованию, когда вы обнаружите, что хотите смоделировать взаимодействие.

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

6 голосов
/ 31 марта 2009

Помните, что хотя гибкость является достойной целью, дополнительная гибкость с IoC и DI (которые в некоторой степени являются требованиями для TDD) также увеличивает сложность. Единственная точка гибкости состоит в том, чтобы сделать изменения вниз по течению быстрее, дешевле или лучше. Каждая точка IoC / DI увеличивает сложность и, следовательно, способствует более трудным изменениям в других местах.

Здесь на самом деле вам нужен Big Design Up to некоторый экстент: определите, какие области могут измениться (и / или нуждаются в обширном модульном тестировании), и спланируйте гибкость там. Рефакторинг для устранения гибкости там, где изменения маловероятны.

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

Теперь области, которые могут меняться или не меняться, зависят от вашей бизнес-проблемы и ИТ-среды. Вот некоторые повторяющиеся области.

  1. Я бы всегда считал внешним интерфейсы, где вы интегрируетесь в другие системы должны быть легко изменяемыми.
  2. Какой бы код не предоставлял бэкэнд для пользовательский интерфейс должен будет поддерживать изменения в пользовательском интерфейсе. Однако прежде всего планируйте изменения в функциональности: не переусердствуйте и не планируйте использование различных технологий пользовательского интерфейса (таких как поддержка интеллектуального клиента и веб-приложения - шаблоны использования будут сильно отличаться).
  3. С другой стороны, кодирование для переносимость в разные базы данных и платформы обычно отходы по крайней мере, в корпоративном сред. Поспрашивайте и проверьте какие планы могут существовать для замены или обновить технологии в рамках вероятный срок службы вашего программного обеспечения.
  4. Изменения в содержании данных и форматах довольно сложны бизнес: пока данные будут иногда меняются, большинство конструкций Я видел, как справиться с такими изменениями плохо, и, таким образом, вы получаете конкретный классы сущностей, используемые напрямую.

Но только вы можете судить о том, что может или не должно измениться.

6 голосов
/ 31 марта 2009

Похоже, ты немного страдаешь от BDUF .

Полегче с Coolade и позвольте ему течь естественным путем.

5 голосов
/ 31 марта 2009

Я обычно нахожу, что мне нужны интерфейсы для "сервисов" - тогда как типы, которые в основном относятся к "данным", могут быть конкретными классами. Например, у меня был бы интерфейс Authenticator, но класс Contact. Конечно, это не всегда так ясно, но это первое практическое правило.

Хотя я чувствую твою боль - это немного похоже на возвращение к темным дням файлов .h и .c ...

2 голосов
/ 31 марта 2009

Я думаю, что самый важный «проворный» принцип - это ЯГНИ («Вам это не нужно»). Другими словами, не пишите дополнительный код до тех пор, пока он действительно не понадобится, потому что если вы напишите его заранее, требования и ограничения вполне могут измениться, когда (если!) Он вам наконец понадобится.

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

Так что, если вы на самом деле тестируете и имеете фиктивный объект, было бы очень полезно, то непременно определите интерфейс, который реализует и ваш фиктивный, и реальный классы. Но не создавайте связку интерфейсов на чисто гипотетических основаниях, которые могут быть полезны в какой-то момент или что это «правильный» дизайн ОО.

0 голосов
/ 31 марта 2009

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

Хорошее практическое правило - создавать интерфейсы из всего, что должно быть подклассом. Это не что-то вроде «всегда делайте интерфейс в таком случае», вам все равно нужно подумать об этом.

Итак, короткий ответ (и это работает как с внутренними вещами, так и с предоставлением API) таков: если вы ожидаете, что потребуется более одной реализации, сделайте ее интерфейсом.

То, что обычно не было бы интерфейсами, было бы классами, которые содержат только данные, как, например, класс Location, который имеет дело с x и y. Вероятность того, что существует другая реализация, невелика.

0 голосов
/ 31 марта 2009

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

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

Эти ответы тоже могут помочь.

0 голосов
/ 31 марта 2009

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

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

...