(Disclaimer!) Данная точка зрения не претендует на роль абсолютной истины и является лишь результатом моего опыта, чтения, наблюдений и размышлений.
Думаю многие знают или слышали о принципах и советах в стиле "Программируйте на уровне интерфейсов, а не реализаций". Хотя в теории, данный принцип кажется полезным и его аргументация звучит логично, но при более глубоком анализе оказывается, что предпосылки лежащие в его основе не реалистичны.
В этой короткой статье, я хочу высказать точку зрения, почему данный принцип в большинстве случаев является ошибочным, создает лишние проблемы и приводит к плохой архитектуре.
Конечный результат не говорит о том, как он был достигнут.
Одно из моих любимых занятий - разбираться во внутреннем устройстве и работе используемых open-source программ и библиотек. В первую очередь, я пытаюсь найти уже имеющиеся статьи или видео, в которых разбирается исходный код, но к сожалению, таких очень мало, поэтому в основном приходится полагаться на собственные силы. Со многими достаточно большими проектами, так просто, это сделать не получится, поэтому я применяю способ "вернуться к началу и пройти исторический путь", к счастью для этого есть git и команда log --reverse. Данный способ полезен тем, что позволяет посмотреть, на то, как выглядела начальная версия, когда она была еще достаточно маленькой и разобраться в ней не составляло большого труда. Серия статей Эволюция докер как раз возникла по такому принципу.
После этого, понимая основы, можно перескакивать по истории коммитов, и проследить, как возникала новая и трансформировалась уже существующая функциональность. Еще один вывод, который можно сделать на основе таких наблюдений, вынесен в заголовок данного раздела - Конечный результат не говорит о том, как он был достигнут. Когда вы видите библиотеку с удобным и логичным интерфейсом, простыми и красивыми абстракциями, в которой практически нет ничего лишнего, и вам кажется, что ее автор(ы) гении, которые изначально все точно спроектировали и продумали, знайте - вы ошибаетесь. В большинстве случаев, конечный результат, это череда длительных итераций, исправлений и переписываний изначально примитивной, неудобной и неоптимальной версии. В таких сложных вещах, сделать все правильно с первого раза практически нереально, что приводит нас к следующей мысли -
Хорошие абстракции рождаются, а не насаждаются
В основе принципа "Программируйте на уровне интерфейсов, а не реализаций", лежит ложное предположение о том, что мы можем удобно, правильно и оптимально спроектировать интерфейс, еще до реализации. Я считаю, что это верно лишь в очень хорошо знакомых или примитивных случаях (которые всегда приводятся как примеры!). Как только ты работаешь с достаточно большим или абсолютно новым проектом, это становится попросту нереалистично, так как нужно представить все возможные места и варианты использования и взаимодействия этих интерфейсов.
Возможно существуют люди, которым это под силу, но я не отношу себя к их числу. Проектирование в UML диаграммах, достаточно бестолковое занятие, так как я не вижу никаких преимуществ перед непосредственным написанием кода, а скорее недостатки, так как код ты можешь запускать и проверять, а работу UML диаграмм можно лишь представлять в голове.
Следуя принципу "Программируйте на уровне интерфейсов, а не реализаций", вы на самом раннем этапе ставите ненужные препятствия, пытаясь цементировать элементы, когда четкого и полного представления задачи еще не сформировано, а это в большинстве случаев приводит к неоптимальным решениям и плохой архитектуре. Я считаю, что в начале должна следовать простая реализация, и лишь по прошествию времени, когда вы лучше поймете задачу и взаимодействие ее элементов, вы сами сможете увидеть повторяющиеся структуры и выделить правильные абстракции и интерфейсы.
Я бы подытожил это так - не нужно насильно насаждать абстракции, пусть они сами рождаются в процессе решения.
Автор: Nick