Почему принцип программирования на уровне интерфейсов в большинстве случаев ошибочен и приводит к плохой архитектуре

    (Disclaimer!) Данная точка зрения не претендует на роль абсолютной истины и является лишь результатом моего опыта, чтения, наблюдений и размышлений.

    Думаю многие знают или слышали о принципах и советах в стиле "Программируйте на уровне интерфейсов, а не реализаций". Хотя в теории, данный принцип кажется полезным и его аргументация звучит логично, но при более глубоком анализе оказывается, что предпосылки лежащие в его основе не реалистичны. 

    В этой короткой статье, я хочу высказать точку зрения, почему данный принцип в большинстве случаев является ошибочным, создает лишние проблемы и приводит к плохой архитектуре.

    Конечный результат не говорит о том, как он был достигнут.

    Одно из моих любимых занятий - разбираться во внутреннем устройстве и работе используемых open-source программ и библиотек. В первую очередь, я пытаюсь найти уже имеющиеся статьи или видео, в которых разбирается исходный код, но к сожалению, таких очень мало, поэтому в основном приходится полагаться на собственные силы. Со многими достаточно большими проектами, так просто, это сделать не получится, поэтому я применяю способ "вернуться к началу и пройти исторический путь", к счастью для этого есть git и команда log --reverse. Данный способ полезен тем, что позволяет посмотреть, на то, как выглядела начальная версия, когда она была еще достаточно маленькой и разобраться в ней не составляло большого труда. Серия статей Эволюция докер как раз возникла по такому принципу.

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

    Хорошие абстракции рождаются, а не насаждаются

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

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

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

    Я бы подытожил это так - не нужно насильно насаждать абстракции, пусть они сами рождаются в процессе решения.

    ***UPDATE***

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

    Всем спасибо за комментарии.

    Комментарии 87

      +1

      Мысль понятна. Не понятно, как автор к ней пришел, точнее к ситуации, когда появилась потребность анализировать подход настолько поверхностно. Тема не развернута, лично я до конца не понял, что именно автора так задело, т.к. проектирование "от ui" является наиболее логичным, когда создаёшь продукт, т.е. отправной точкой для mvp должно быть удовлетворение конкретных потребностей продукта (сервиса), а все навороты и фичи - уже потом😳

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

        +22

        Здесь речь идет лишь о принципе программирования и программных, а не ui интерфейсах.

          +12

          Мысль понятна. Не понятно, как автор к ней пришел

          "Одно из моих любимых занятий - разбираться во внутреннем устройстве и работе используемых open-source программ и библиотек."

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

          проектирование "от ui" является наиболее логичным

          Не все программы и библиотеки имеют "ui"

            0

            Но все имеют пользователей. В случае библиотеки - замените UI на «внешние интерфейсы», в случае бэкенда - на API. Суть одна - есть клиент и весь код нужен чтобы на какой-то его запрос давать какие то ответы - то какого типа есть запросы у пользователя и какого типа должны быть на них ответы и есть user interface. А то что пользователь - программист и сам внешние методы вашей либы дергает - это уже детали.

            +6

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

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

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

              +6

              Программирование на уровне интерфейсов -- огромное преимущество ООП, без этого сегодня ни один большой проект не обойдётся

              Я бы с вами не согласился, интерфейс понятие более общее и существуют в отдельности от ООП. Как тогда по вашему работают все большие проекты написанные без использования данной парадигмы?

              Если совсем кратко, то я бы еще охарактеризовал так:

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

                +2

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

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

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

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

                  +2

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

                    0

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

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

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

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

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

                        0

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

                          0

                          Я не знаю в каких проектах работаете лично вы. Но я бы не сказал что у нас количество подобных интерфейсов невелико :)


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


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

                            0

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

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

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


                              Я говорю о применении данного принципа при написании функций, классов и структур данных, когда в преждевременном введении интерфейсов нет никакой необходимости.

                              Ну вот банально если вы работаете с любой более-менее модульной архитектурой, плагинами, юнит-тестами/моками или тем же dependency injection, то вам от интерфейсов скорее всего никуда не деться. С другой стороны создание и подержка интерфейсов практически "бесплатна" и не занимает время если у вас есть IDE с адекватным функционалом.

                                0

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

                                  0

                                  С другой стороны создание и подержка интерфейсов практически "бесплатна" и не занимает время если у вас есть IDE с адекватным функционалом.

                                  Да, и еще, что я заметил на своем опыте, что преждевременное введение интерфейсов и локальных оптимизаций(привет Кнуту), скрывает от тебя более глобальные оптимизации. И если взять и переписать решение "в лоб", без абстракций, то можно увидеть намного более удачные абстракции, чем те, что были прежде. А это IDE вам не поможет обнаружить.

                                    0
                                    И если взять и переписать решение "в лоб", без абстракций, то можно увидеть намного более удачные абстракции, чем те, что были прежде. А это IDE вам не поможет обнаружить.

                                    Во первых это уже зависит от IDE и тулинга в принципе. Различные анализаторы они тоже развиваются и на месте не стоят. И такие вещи это скорее задачи техлидов/архитектов, а не среднего программиста.


                                    А во вторых чем вам интерфейсы то в данном случае мешают? Вам же никто не запрещает и на конкретную реализацию смотреть. Это же не так что если интерфейс есть, то всё что за ним автоматом становится невидимым....

                                      0

                                      Во первых это уже зависит от IDE и тулинга в принципе. Различные анализаторы они тоже развиваются и на месте не стоят

                                      Я сомневаюсь, что такие анализаторы существуют, которые могут находить более глобальные оптимизации автоматически. К тому же, понятие оптимизации, зависит от конкретной цели. У всего есть обратная сторона, и в одном случае изменение может считаться оптимизацией, а в другом нет.

                                      А во вторых чем вам интерфейсы то в данном случае мешают? Вам же никто не запрещает и на конкретную реализацию смотреть. Это же не так что если интерфейс есть, то всё что за ним автоматом становится невидимым....

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

                                        0
                                        Я сомневаюсь, что такие анализаторы существуют, которые могут находить более глобальные оптимизации автоматически.

                                        Они как минимум могут подсказать вам где стоит поискать.


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

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


                                        А вот если у вас интерфейс это просто "контракт" и существует только одна реализация, то на мой взгляд вообще нет никакой разницы в контексте оптимизации и нахождения "более удачных абстракций". В куче IDE даже можно настроить что если реализация одна, то "навигация" по дефолту идёт сразу на неё, а не на интерфейс.

                                          0

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

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

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

                                            А я не оосбо понимаю что такое "преждевременное введение без необходимости" в вашем понимании.


                                            Вот какая разница есть у вас интерфейс с сигнатурой функции или просто публичная функция с такой же сигнатурой? Какие конкретно недостатки имеет первый вариант по сравнению со вторым?

                                              0

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

                                              Я бы с вами не согласился, интерфейс понятие более общее и существуют в отдельности от ООП. Как тогда по вашему работают все большие проекты написанные без использования данной парадигмы?

                                              Под словом интерфейс предполагается совокупность методов и функций и тогда вопрос теряет смысл.

                                                0

                                                Я всё ещё не особо понимаю в чём проблема. Вот у нас есть вариант:


                                                без интерфейса
                                                class Foo
                                                {
                                                     public void GetFooData(int dataId)
                                                    {
                                                       .....
                                                    }
                                                }

                                                И вариант:


                                                с интерфейсом
                                                class Foo :IFoo
                                                {
                                                     public void GetFooData(int dataId)
                                                    {
                                                       .....
                                                    }
                                                }
                                                
                                                interface IFoo
                                                {
                                                  void GetFooData(int dataId);
                                                }

                                                Это "интерфейс в узком ООП смысле"? Или нет?


                                                И какие конкретно недостатки будет иметь вариант с интерфейсом по сравнению с вариантом без оного?

                                                  +1

                                                  Недостаток имеет вариант, когда вы заранее решили, что вам нужен именно метод GetFooData, и что он должен принимать именно int dataId, хотя возможно вместе с id, во многих местах, вам будет нужен еще и String name и оптимальнее было бы сделать структуру данных содержащую id и name. Но так как вы уже определили такой интерфейс (в глобальном смысле) взаимодействия с Foo, то в остальных местах, где вам нужно еще и name, вы можете добавить дополнительные ненужные и неоптимальные функции, которые получают name. А то, что у этого сразу определен еще и ООП интерфейс уже вторично. Естественно с одним, двумя и тд классов, это не кажется большой проблемой, но мы же говорим о более крупных проектах, где их десятки и сотни, и они взаимодействуют друг с другом.

                                                    0
                                                    Недостаток имеет вариант, когда вы заранее решили, что вам нужен именно метод GetFooData, и что он должен принимать именно int dataId, хотя возможно вместе с id, во многих местах, вам будет нужен еще и String name и оптимальнее было бы сделать структуру данных содержащую id и name.

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


                                                    Ну или почему вы считаете что функцию я могу менять как хочу, а интерфейс нет? В чём разница?


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

                                                    Или я просто при необходимости меняю интерфейс. Это же не запрещено.

                                                      0

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

                                                      Ну или почему вы считаете что функцию я могу менять как хочу, а интерфейс нет? В чём разница?

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

                                                      Но так как вы уже определили такой интерфейс (в глобальном смысле) взаимодействия с Foo

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

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

                                                        В смысле? Вы сначала пишите отдельны части программы без всяких публичных методов и только потом когда они готовы пытаетесь собрать их в единое целое? Или как?


                                                        Тогда я бы сказал что ваша статья не о интерфейсах, а о том что иногда bottom-up подход логичнее чем top-down. Ну ок, да. В отдельных случаях :)

                                                          0

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

                          0

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

                        +2

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

                        Вы согласны?

                        Скрытое оскорбление

                        Просто согласитесь, что статья незавершённая, содержание противоречит заголовку. Мнение автора не считается аргументом.

                          0

                          Согласен, я добавил подробный комментарий в конце статьи.

                    –25

                    Афтар, почитай книги дяди Боба.

                      +4

                      Мне кажется, что посыл автора в целом верный, но не понятно, как он относится к заголовку.

                      Проблема относится скорее к сложностям развития и изменения требований для программных компонентов.

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

                      При изменении деталей реализации может потребоваться изменить интерфейс, абстракции не идеальны. А может и не потребоваться.

                      Авторы популярных библиотек просто прошли в этом чуть дальше, а их абстракции лучше протестированы и обточены.

                        +4

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

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

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

                        Какое отношение масштаб проекта имеет к интерфейсу (API), который всегда относится к отдельному классу? То есть, речь об интерфейсах (во множественном числе) не идет никогда. Речь идет об одном интерфейсе в каждый момент времени, и никаких серьезных проблем масштабирования в этом месте не существует.
                          +1

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

                            +4

                            по средствам

                            Посредством.

                              0
                              Так и что? Работа с интерфейсами а не реализациями вполне может быть проделана для отдельного класса. Никто не заставляет вас делать это во всем проекте, даже если это вдруг полезно.
                            +10
                            ошибочен и приводит к плохой архитектуре

                            Что метод приводит к плохой архитектуре — вообще в статье не продемонстрировано. Показано, что автору так неудобно, он что-то не понимает, и т.п. Это вполне бывает, но это разные вещи. Если кто-то не умеет умножать — ну так он не умеет умножать, а не умножение не работает.
                              0

                              Не буду спорить, для этого в начале статьи есть дисклеймер :)

                                +2
                                Не, ну вы могли бы это попробовать показать. Возможно что и приводит — но как именно, в тексте не видать. Даже если это будет ваш опыт — так все равно будет лучше.
                                  0

                                  Согласен на счет отсутствия примеров, добавил комментарий в конце статьи.

                              +12

                              Тут неплохо бы пойти дальше и признать, что код надо непрерывно выбрасывать и кодить заново, причём как на уровне отдельных задач, так и на уровне целых систем.

                                +3

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

                                +1

                                DEL

                                  +4

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

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

                                  То есть сначала писать на уровне реализации, а потом добавить интерфейс? И как это внедрять, расширять? Покажите примером.

                                    0

                                    Ок, может быть в будущем напишу более подробно и с примерами, так как их у меня хватает.

                                    +1

                                    В Enterprise Java на уровне интерфейсов программировали исторически потому, что тестировать проще, подсовывая тестовые реализации. Впрочем, с развитием Mockito это причина ушла в прошлое и остался cargo cult.

                                      0

                                      Далеко не везде так можно сделать. Иногда использование интерфейсов - это единственный способ подставить мок.

                                        0

                                        По-английски говорят, это tail wagging the dog. Позорище, короче.

                                        +2

                                        Не все упирается в тестирование. Отдельные интерфейсы могут быть нужны:

                                        • потому что один класс может имплементировать несколько интерфейсов (например, для соблюдения ISP клиенты зависят от точечных интерфейсов, но имплементация имеет смысл в виде одного класса);

                                        • потому что у вас может быть несколько реализаций одного интерфейса (например, Стратегия);

                                        • потому что вы хотите разделить слои, и, например, интерфейс репозитория у вас находится в слое бизнес-логики, а его имплементация – в слое данных / инфраструктуры.

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

                                        0

                                        ну абстракции и вообще инкапсуляция - это не только про проектирование систем

                                          +2
                                          Данная точка зрения не претендует на роль абсолютной истины и является лишь результатом моего опыта, чтения, наблюдений и размышлений.

                                          А зря, звучит как отказ от своей позиции. В конце концов, если вы говорите о чем не как о факте, а как о какой-то позиции (пусть даже "своей"), то это не ваша позиция, т.к. вы от нее абстрагировались, отделились


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

                                            +1

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

                                            +4

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

                                            Вообще при проектировании функций мне помогает вопрос "как ты будешь это использовать". От этого будет зависеть реализация.

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

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

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

                                            Так что я считаю, что в проектировании интерфейсов, полезнее отталкиваться "от спроса", а не "от предложения".

                                              +2
                                              Вообще при проектировании функций мне помогает вопрос "как ты будешь это использовать"

                                              Стив Джобс такой подход использовал. Имхо, единственный адекватный подход при проектировании, получаются действительно удобные вещи

                                                0

                                                Это хорошо работает с публичными интерфейсами и api, но всю внутреннюю архитектуру, так не построишь.

                                                +1

                                                Этот комментарий вполне можно превратить в статью. Или свернуть до цитаты Дональда Кнута premature optimisation is the root of all evil.

                                                +4
                                                Напомнило историю из тех времён, когда я студентом работал в одной компании. Я писал сборный визуализатор/редактор, который выводил/позволял редактировать информацию из разных источников. Одним из источников была… э-э… картография. Собственно, картографический слой был основным и самым алгоритмоёмким, поэтому растеризацией картографии занимался другой человек, с большим опытом по этой части. Там много зависимостей от разрешения/масштаба/технологических особенностей устройства вывода (монитор, принтер, плоттер), обусловленных юзабилити и допустимостью разных моделей интерполяции, например. Короче, в какой-то момент меня осенила «гениальная» идея: нафига мы столько служебной информации гоняем из модуля в модуль? Ладно, масштаб — но разрешение и всё остальное? И я написал в декларациях колбэк-интерфейс из одного метода: Point VirtualPointToDevicePoint(Point pt). Мощь этого решения была невероятной: можно было ничего не переписывая выводить картинку на любое гипотетическое устройство, хоть на параболоид инженера Гагарина! Можно было не думать о каких-то там масштабах и прочих несущественных деталях! Поскольку я действовал не со зла, а по неопытности, мне удалось обмануть не только себя, но и того чувака, который писал растеризатор. С моей-то стороны всё действительно выглядело просто и универсально, а вот с его… Через неделю он пришёл злой, небритый, невыспавшийся и без обиняков заявил, что после того, что по моей милости он делал со своими алгоритмами все выходные, он, как честный человек должен на них жениться. И что он уже близок к тому, чтобы и со мной проделать то же самое. Пришлось, короче, вернуться к старому интерфейсу. Не понимают эти погрязшие в нюансах программисты тонкую ранимую душу архитектора!
                                                  +6
                                                  Просто не надо возводить эвристические принципы в абсолют.

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

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

                                                    Просто не надо возводить эвристические принципы в абсолют.

                                                    Абсолютно согласен.

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

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

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

                                                      +5
                                                      Я утверждал, что принцип программирования — «интерфейсы, до реализации» в большинстве случаев ошибочен

                                                      Так, стоп, давайте определимся — какой именно принцип ошибочен? В какой формулировке?
                                                      Потому что сама по себе фраза «интерфейсы до реализации» не может быть ошибочной хотя бы по той простой причине, что она не содержит никакой конкретики. В неё нигде не говорится «нужно полностью и детально проработать все интерфейсы до того, как вы вообще приступите к реализации». В такой формулировке принцип действительно вреден. Но тут вы спорите не с самим принципом, а с конкретной его интерпретацией. И делаете вывод, что он по сути неверен.
                                                      А какую вы тогда предполагаете альтернативу? Приступать к реализации не имея даже общего представления интерфейса? Ну, удачи ))

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

                                                      Так сами принципы сомнительные? Или всё-таки проблема в тех людях, которые не умеют готовить эти принципы, и при этом считают их серебрянной пулей?
                                                      Карго-культ — это заблуждение, но реальные самолёты вполне себе успешно летают по небу.
                                                    +4

                                                    Почему принцип программирования на уровне интерфейсов ошибочен и приводит к плохой архитектуре

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

                                                    Содержание статьи не соответствует заголовку, на мой взгляд

                                                      +2

                                                      Я не автор, но пример из виденного могу привести.

                                                      protocol ProductProtocol {
                                                        var cost: Decimal { get }
                                                        var lenght: Double { get }
                                                        var cardHolderName: String { get }
                                                      }
                                                      
                                                      class SaleProduct: Codable, ProductProtocol {
                                                        var cost: Decimal = 0 // приходит с сервер
                                                        var lenght: Double = 0 // приходит с сервер
                                                        var cardHolderName: String { "" } // всегда пустая строка
                                                      }
                                                       class GiftCard: Codable, ProductProtocol {
                                                         var cost: Decimal = 0 // приходит с сервера
                                                         var lenght: Double { 0 } // всегда 0
                                                         var cardHolderName: String = "" // приходит с сервера
                                                       }
                                                      
                                                      //где в коде функция
                                                      func showInfo(product: ProductProtocol) {
                                                        if product is SaleProduct {
                                                          self.showSaleProductInfo(product)
                                                        } else if product is GiftCard {
                                                          self.showGiftCardInfo(product)
                                                        }
                                                      }
                                                      

                                                      По примеру:

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

                                                      2 - в функции по отображение информации об элементе, была проверка на тип. То есть типы изначально разные и работа с ними ведётся в по разному оформленных окнах.

                                                      Когда это на одной странице, это ещё можно быстро понять, а когда такое размазано по коду то это приводит к курьёзам, когда у SaleProductInfoView появляется показ отдельного окна, для редактирования cardHolderName.

                                                      У меня так же есть примеры хорошего использования интерфейсов.

                                                        0

                                                        Простите, но это просто плохой код. Наличие плохих интерфейсов говорит лишь о том, что их просто плохо спроектировали. А не о том, что интерфейс - это плохо.

                                                      +1

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

                                                      Странно ожидать "идеального решения" в постоянно изменщихся бизнес-процессах.

                                                        +1

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

                                                        +3

                                                        Разделяю мнение автора. Так же понимаю почему в комментариях люди жалуются что автор не привёл конкретных примеров. Мне кажется что у автора произошла некоторая профессиональная деформация в следствии изучения кода популярных опенсоурсных проектов. Автор знает как именно выглядет достойный код и на уровне интуиции понимает как подобный код писать. Но когда любитель посмаковать опенсоурсные проекты пытается объяснить что такое "достойный код" людям, которые не особо интересуются опенсоурсными решениями, то получается как в анекдоте про чукчу, который пытался объяснить другим чукчам что такое апельсин.

                                                          +1

                                                          Я на этот вопрос смотрю под другим углом - интерфейсы, как элемент кода, предназначены не для программ, а для людей.

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

                                                          Таким образом, интерфейсы идеологически разделяют систему на части, которые разные люди могут развивать независимо. И такие разделяющие интерфейсы обязаны быть, и они должны быть стабильными, иначе система перестанет нормально развиваться - в небольших системах все на себя перетянет один человек (т.е. dungeon master), а в больших наступит типичный легаси-хаос. И да, такие интерфейсы зачастую приходится проектировать "пальцем в небо", и с ними приходится мириться.

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

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

                                                            +1

                                                            Я думаю, такие мысли рождается в голове, когда человек встречается с тем, что он воспринимает как оверинджиниринг. И обычно оверинджинирингом занимаются другие, а я-то д'Артаньян и пишу понятный код (мне же он понятен!). И тут есть два варианта: 1) код коллеги -- действительно оверинджиниринг, учитывающий миллионы гипотетичных кейсов в будущем -- но это проблема коллеги и к интерфейсам отношения не имеет 2) автор таких мыслей не обладает полными знаниями по бизнес-контексту и не понял архитектуры, поэтому всё списал на "какой-то дикий оверинджиниринг" -- очень удобно, т.к. думать не нужно.

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

                                                              +2

                                                              Я думаю, это отчасти некий спор о терминах в рамках воображаемой концепции "мы сейчас спроектируем интерфейс, а потом уже и реализация подъедет". Если же переходить к практическим сценариям, то с одного конца спектра будет "waterfall глазами его противников" (т.е. всё проектируем на бумаге, затем пишем), а с другого конца -- "органический рост", где всё как бы "само" растёт. Но даже в этой концепции как только у вас появляются компоненты А и Б, им же приходится как-то взаимодействовать? Значит, вы всё равно садитесь и делаете интерфейс. То есть вопрос в том, что такое "отталкиваться от", и где мы уже не "отталкиваемся".

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

                                                                +4

                                                                Мне кажется, я понял, что хотел сказать автор. Во времена моей молодости, когда я только начал работать со Спрингом (core Java уже был в бекграунде), то во многих книгах, на которых я учился было сказано, что каждая имплементация некоторой бизнес логики (спринговый бин другими словами) должна сопровождаться интерфейсом, я так это запомнил. Доводов к такому подходу было много - это и TDD, это и мокирование в юниттестах, это и реализация нескольких имплементаций одного интерфейса, а также проектирование перед реализацией. И это настолько запало в памяти, что я каждому бину присоединял интерфейс, чаще писал интерфейс, затем реализацию, а потом кромсал и то и другое в процессе рефакторинга и изменения требований. А сильно потом понял, что 95% моих классов с бизнес логикой являются внутренними и не нуждаются в интерфейсах. Они имеют одну единственную имплементацию, мокируются в спринге без всяких проблем, я чаще всего не пишу через TDD. Но зато это сильно облегчило рефакторинг. И если уж вдруг встала необходимость иметь интерфейс к имплементации, то моя IDE может выделить его с помощью пары кликов мыши.

                                                                  +2

                                                                  Мне кажется что это частный пример "разработка снизу-вверх vs сверху-вниз". А-ля "делали-делали, а api сказал что так не бывает" vs "все куски работали, а собрали чудищо".

                                                                    +1

                                                                    Как-то логика ускользает.

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

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

                                                                      0

                                                                      Если следовать принципам SOLID и использовать всю мощь OOP то у вас все получится красиво!

                                                                        0

                                                                        Без картинок? 😫😫😫😫

                                                                          +3

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

                                                                          Не надо создавать интерфейс там, где не предполагается альтернативной реализации функционала.

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

                                                                            0

                                                                            Это немного не так: мы всегда создаём интерфейс, когда добавляем публичные методы. Интерфейс -- литерали -- это всё, что видит клиент, точка взаимодействия двух сторон.

                                                                            Многие языки программирования поддерживают интерфейсы на уровне системы типов, но с декларацией interface или без неё, вы создадите их. Декларация тут ещё и формальный ритуал, который призван символизировать важность выставления тех или иных методов в паблик.

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

                                                                            +1

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

                                                                            Эм... что? Кто сказал, что наличие интерфейсов означает "цементирование элементов"? Если нужно - все рефакторится, в том числе и интерфейсы.

                                                                            Напомнило недавний разговор из телеграм-канала по PHP, где, внезапно, ООП противопоставляли DDD.

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

                                                                              +1

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

                                                                              Как вообще можно браться за проект без четкого и полного представления задачи? Написать "на коленке", после подумать и после всё переписать.... как это по нашему... верно?

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

                                                                                Приведу несколько примеров из жизни за последнее время:
                                                                                1. Переделывал хранение локализации из properties файлов, в бд -> просто подменил реализации, без необходимости исправлять что-то по всему проекту.
                                                                                2. Для стороннего api захотели сделать фолбэк, т.к. оно часто падало, просто обернули реализацию в прокси, опять же без необходимости что-то менять.
                                                                                3. Сторонняя команда, реализовала хранилище нужного нам сервиса на другой бд (ускорив тем самым перфоманс), мы просто поменяли у себя зависимость, и все само заработало.
                                                                                  0

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

                                                                                    0

                                                                                    Программирования в терминах интерфейсов и позволяет относительно малой кровью эволюционировать архитектуре.

                                                                                      +1

                                                                                      Понимаю о чем вы, т.к. сам часто обвиняю себя в том, что не могу с нуля написать "правильно", как, по-моему мнению, пишут другие, лучшие люди.

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

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

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

                                                                                        +1

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

                                                                                        В этом смысле да, мы можем и должны проектировать интерфейсы сервисов независимо от их реализации. Тем самым перекладывая сложность со стороны клиентов на сторону сервисов.

                                                                                        Если ситуация такова, что мы разрабатываем сервисы, ещё не имея понимания, какие интерфейсы будут нужны клиентам, то да, их придётся по 500 раз переделывать, это нормально, ни о каком цементировании речи нет.

                                                                                          0

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

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

                                                                                          Проектирование в UML диаграммах, достаточно бестолковое занятие, так как я не вижу никаких преимуществ перед непосредственным написанием кода, а скорее недостатки, так как код ты можешь запускать и проверять, а работу UML диаграмм можно лишь представлять в голове.

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

                                                                                            0

                                                                                            Когда я начал изучать скалу, мне было совершенно непонятно, почему там сначала интерфейсы(trait) пишут, а потом уже реализации. После питона было дико неудобно и казалось, что я напрасно трачу время, лучше сразу класс с логикой запилить. А потом я глянул видео Scott Wlaschin — Talk Session: Domain Modeling Made Functional и понял, что интерфейсы должны писаться не только программистами, а именно специалистами по бизнесу, для которого делают проект, потому что они разбираются в теме гораздо лучше программистов. Тогда интерфейсы имеют больше смысла, так как отражают бизнес модель.

                                                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                                            Самое читаемое