2009-03-31 3 views
8

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

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

+0

Итак, теперь все ... переключают компьютеры и читают код друг друга! –

ответ

7

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

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

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

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

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

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

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

6

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

Прощайте с охладителем и дайте ему протекать естественным путем.

5

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

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

0

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

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

-1

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

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

Those answers может помочь.

+1

-1 дизайн всегда должен начинаться с интерфейсов. –

6

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

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

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

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

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

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

2

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

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

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

+0

Пожалуйста, изложите утверждение: «все это добавляет сложности вашему коду, что затрудняет понимание и изменение». Это ключевое слово в вашем ответе, но не дает никаких оснований для того, почему * это правда. –

0

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

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

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

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