Должен ли я написать интерфейс API перед реализацией?

14

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

И если это так, то в случае использования сторонних библиотек (например, Lidgren) я должен также обернуть их в интерфейсы и разрешить их через контейнеры IOC, или это нормально, чтобы выставить их интерфейсам?

Дэн Кладовая
источник
По моему личному опыту - хорошо сначала спроектировать архитектуру - ответственность каждого класса. Вам не нужно записывать это, просто подумайте об этом или набросайте это на бумаге. Тогда речь идет о личных предпочтениях, но я рекомендую сначала писать комментарии к документам для каждого метода, который вы начинаете внедрять. Написание документов действительно заставляет задуматься о функциональности, прежде чем вы начнете писать код.
Sulthan
Да, и запрограммируйте интерфейсы (или абстрактные классы в этом отношении) перед их реализацией. Это помогает передавать поток сообщений от клиента к серверу и, наоборот, «правильно», прежде чем увязнуть (и инвестировать) в реализации. Очень хорошее слайд-шоу по этому вопросу: как разработать хороший API и почему это важно
Марьян Венема,

Ответы:

8

К сожалению, вы обнаружите, что это часто сводится к личным предпочтениям.

То, что вы описали до сих пор, кажется хорошим. На самом деле, если вы хотите (и я рекомендую это), вы можете использовать следующий подход:

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

Вы концентрируетесь на попытке написать более «организованный» код. Следующий TDD поможет вам в этом.

Некоторые дополнительные очки:

  • Контейнеры IoC удобны. Используйте их и DI столько, сколько сможете.
  • Как обернуть 3 - библиотеки. Это ослабит связь между вашим кодом (код, который вы контролируете) и кодом третьей стороны (код, который вы не контролируете)
MetaFight
источник
1
Это было то, что я первоначально думал, но мне сказали, что это нарушит принцип ЯГНИ. Проблема, с которой я сталкиваюсь во многих моих проектах, которые так и не были завершены, заключается в том, что они быстро перестают обслуживаться с количеством написанного мной большого двоичного кода, потому что я не организовал его должным образом или не спланировал свой план атаки.
Дэн Пантри
какая часть будет нарушать YAGNI?
MetaFight
Упаковка сторонних библиотек.
Дэн Кладовая
2
Я думаю, это сводится к следующему: каковы шансы на изменение сторонней библиотеки? Если есть вероятность этого 0%, то обязательно YAGNI. Но это редко так. Кроме того, упаковка сторонних библиотек может сделать ваш другой код более легким для модульного тестирования (если, например, вы не можете
смоделировать
1
@DanPantry: Обертывание сторонних библиотек - это не нарушение YAGNI, а крайне необходимая защита от «заражения сторонних библиотек вашим собственным кодом». Речь идет не только о возможности поменять библиотеку, но и о том, что MetaFight также защищает от изменений в более новых версиях библиотеки, которые в противном случае потребовали бы изменений в вашем собственном коде. Оборачивая библиотеку (и особенно ее конкретные типы: классы, перечисления, структуры и т. Д.), Вы изолируете свой собственный код и имеете одну точку, которую нужно изменить при изменении библиотеки (по любой причине).
Марьян Венема
13

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

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

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

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

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

Килиан Фот
источник
Мне нравится ваша точка зрения о том, что SE в основном заключается в достижении желаемого эффекта таким образом, чтобы впоследствии можно было работать с исходным кодом. Хотелось бы мне так хорошо это сформулировать на моей последней работе, где я всегда боролся за чистый код!
MetaFight
Существует ли соглашение об именах для API, которые являются просто интерфейсами, которые я буду использовать повсеместно? Мол, если я делаю шаблон команды, я называю это «commandables»?
Снуп
@StevieV Существуют различные варианты, например, IBlahреализованные Blahили Blahреализованные BlahImpl. Мне не нравятся оба, и я склонен использовать Blahреализованный OralBlah, WrittenBlahили ASLBlah. Но, как обычно, более важно соответствовать существующей кодовой базе и ожиданиям, чем любому общему стандарту.
Килиан Фот
4

Вместо того, чтобы рабски просто программировать интерфейсы, почему бы не заглянуть в Test Driven Development / Design (TDD)?

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

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

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

Что касается использования сторонних библиотек, я настоятельно рекомендую оборачивать их в свои абстракции, где это уместно; и не позволяйте клиентам вашего API «знать» о них.

Удачи!

[править: видел ответ megaflight - полностью согласен]

rupjones
источник
2
TDD неявно заставляет вас думать с точки зрения интерфейса, а не имплементации, хотя это не может быть формальным объявлением «интерфейса».
DougM
1
Это отличный ответ. +1 за предложение TDD, которое, как мне кажется, является решением реальной проблемы ОП, касающейся того, с чего начать при работе над новым проектом, и я бы +1 снова, если бы мог за «Использование TDD, заставит вас изучить, где такие интерфейсы важны, и где это, честно говоря, не имеет значения. "
Бенджамин Ходжсон
2

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

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

Кайл Джонсон
источник
Я думаю, люди под -use интерфейсов. Если вы хотите создавать многократно используемые элементы, интерфейс - это не только приятное дополнение, но и главное, о чем нужно позаботиться. Помимо фактической реализации, конечно.
JensG
1

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

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

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

Питер Смит
источник