Если совсем строго - там про API. Который так-то тоже "interface".
Это да, речь о публичном контракте модуля. Отдельный хороший вопрос что такое модуль в мире дотнета. Вот в турбопаскале были прямо модули которые так и назывались и у которых был интерфейс в смысле публичная часть.
Но, механизм у Мартина это абстрактный класс (ну или интерфейс, в разных языках по разному). О чем он и пишет
Figure 4 shows a more appropriate model. Each of the lower level layers are represented by an abstract class. The actual layers are then derived from these abstract classes. Each of the higher level classes uses the next lowest layer through the abstract interface.
Или более поздняя формулировка
In a statically typed language, like Java, this means that the use, import, and include statements should refer only to source modules containing interfaces, abstract classes, or some other kind of abstract declaration.
Тут еще есть какие-то "some other kind of abstract declaration", но что это совершенно непонятно.
А дальше вот такое правило
Don’t refer to volatile concrete classes. Refer to abstract interfaces instead. This rule applies in all languages, whether statically or dynamically typed.
Так что речь у него именно об абстрактных классах/интерфейсах.
Ну вот вы же в следующем предложении пишете про книгу, а я вам пишу про статью.
В книге вам приводят пример того что может быть абстрактным слоем на примере кода приложения.
В какой из книг и где именно? Формулировки у него менялись (в сторону размытия), но про абстрактные интерфейсы у него как минимум в "Agile.Principles.Patterns.and.Practices", "Agile.Principles.Patterns.and.Practices in C#" и в "Clean Architecture"
Если грамотно разбить библиотеки по пакетам, то DIP позволит их использовать и поддерживать более эффективно.
Типичное утверждение про solid - за все хорошее и против всего плохого. Но все же есть вопросы. Если разбить неграмотно, то DIP не позволит? А грамотно это как именно? Таким принципам грош цена.
А про что там абстракция, если там написано abstract interface? Абстрактный класс с частичной реализацией не катит - абстракции не "должны зависит от деталей".
Пример "наивного" (в трактовке Мартина) разделения на слои это как раз отсутствие абстрактного интерфейса. А "правильное" (опять же в трактовке Мартина) разделение на слои это как раз с наличием абстрактного интерфейса.
Figure 11-2 shows a more appropriate model. Each of the upper-level layers declares an abstract interface for the services that it needs. The lower-level layers are then realized from these abstract interfaces. Each higher level class uses the next-lowest layer through the abstract interface. Thus, the upper layers do not depend on the lower layers. Instead, the lower layers depend on abstract service interfaces declared in the upper layers. Not only is the transitive dependency of PolicyLayer on UtilityLayer broken, but even the direct dependency of the PolicyLayer on MechanismLayer is broken.
Тут еще надо не запутаться между интерфейсом в смысле публичного контракта класса или модуля и интерфейсом в смысле абстрактного класса.
Я настоятельно рекомендую почитать оригинальную статью Мартина, там написано почему должен быть абстрактный интерфейс (абстрактный класс в плюсах), и зачем нужна инверсия владения интерфейсом. Зачем чисто плюсовые приколы в других языках вопрос риторический.
Это цитаты из книги Мартина. Если вы не в курсе, он писал на английском. А если почитаете его первоначальную статью, там даже написано почему интерфейс должен быть абстрактным классом.
На каждую новую фичу jar не будет, но новый class-файл - да, будет.
Нарушаете заветы дяди Боба!
The binary executable version of the module, whether in a linkable library, a DLL, or a Java .jar, remains untouched
В том, что в большом проекте эти наследования приводят к ООП-HELL.
Ну пусть будет не наследование, а еще одна реализация интерфейса, это уже зависит от контекста.
Огромное количество наследников, разобраться в их взаимосвязи и что откуда вызывается и какой путь выполнения - становится совершенно невозможно.
Тоже самое можно сказать про огромное количество интерфейсов и их реализаций.
А еще многое зависит от проекта. Если это публичная библиотека ситуация одна. Если некий внутренний проект, ситуация другая. А у Мартина про это вообще ни словечка. ИМХО правильный подход это когда сравниваются разные способы реализации и выбирается наиболее уместный в данном конкретном случае. И в зависимости от конкретной ситуации - новый класс, новый наследник, новая реализация интерфейса, может банально новая перегрузка метода. А у Мартина просто дичь - нельзя менять бинарник!
Любое использование интерфейса всегда разворачивает зависимость на 180 градусов.
Мартин считает иначе, цитату я привел. Я не к тому, что мнение Мартина о том как делать правильно автоматически верное. Но если мы обсуждаем принципы Мартина мы должны обсуждать его точку зрения.
Иначе получается совсем смешно. Взят банальный прием ООП, назван непонятным именем и теперь надо заплатить денег дяде Бобу чтобы он объяснил что имеется в виду.
Поэтому в хорошей архитектуре всегда зависимости между модулями выстраиваются однонаправлено.
Это "слоистая" архитектура и в ней инверсия зависимостей и/или использование абстрактного класса/интерфейса прямого отношения не имеют. Можно разделить код на слои, выстроить зависимости однонаправленно и все это без абстрактных классов.
Ну а когда и на каком языке это написано, значения не имееет для данного принципа.
One might complain that the structure in Figure 3 does not exhibit the dependency, and transitive dependency problems that I claimed. After all, Policy Layer depends only upon the interface of Mechanism Layer. Why would a change to the implementation of Mechanism Layer have any affect at all upon Policy Layer?
In some object oriented language, this would be true. In such languages, interface is separated from implementation automatically. In C++ however, there is no separation between interface and implementation. Rather, in C++, the separation is between the definition of the class and the definition of its member functions
Как минимум OCP и LSP это вообще не идеи дяди Боба.
Потом я прочитал книги Дяди Боба и мне все стало понятно.
Мне непонятно вот это
They are closed for modification. Extending the behavior of a module does not result in changes to the source, or binary, code of the module. The binary executable version of the module whether in a linkable library, a DLL, or a .EXE fileremains untouched.
Как вы расширяете поведение модуля без изменения бинарников?
Сова это когда любое использование интерфейса (интерфейса в смысле абстрактного класса) называется инверсией зависимостей. Сам же Мартин пишет вот что
Notice that the inversion here is not just one of dependencies, it is also one of interface ownership. We often think of utility libraries as owning their own interfaces. But when the DIP is applied, we find that the clients tend to own the abstract interfaces and that their servers derive from them.
А это уже достаточно спорно. И если в плюсах (по крайней мере в плюсах времен Мартина) у этого есть смысл, то в тех же шарпах смысла в этом нет.
А не является ли это обычным симптомом паттернофилии, когда вместо анализа проблемы и возможных путей ее решения лепят пытаются лепить паттерны из умной книжки? Потому что паттерны это круто. И дело вовсе не в синглтоне.
Это если хорошенько натянуть сову на глобус. Что-то типа - используйте интерфейсы/абстрактные классы там где надо, а где не надо, не используйте. Хороший принцип? Хороший. Знание настолько общего принципа как-то помогает писать хороший код? Нет.
Это не говоря о том, что изначально Мартин писал исключительно про плюсы и сам писал что в других языках проблемы нет. В интернетах есть его оригинальная статья, абзац " Separating Interface from Implementation in C++".
Вот! Когда Мартин еще сам писал код, а не учил этому других, многое было иначе. В частности систем контроля версий в современном понимании этого слова не было. Системы были, конечно, но были они откровенно убогие по нынешним меркам. Да и даже убогие использовались далеко не везде. А в такой ситуации одновременная работа с файлом (классом, модулем), когда несколько человек одновременно вносят правки превращается в мучение. И подход - разбейте класс/модуль на части чтобы исключить одновременные правки это реально работающая вещь.
Что касается OCP, то придуман он был Мейером и у него явно сказано что это про организацию работы. Мартин же довел принцип до абсурда - нельзя чтобы бинарники менялись при добавлении нового функционала.
Потому что мало где нужен, да и сейчас повсеместно DI контейнеры, пишешь что-то вроде container.AddSingleton<MySingletonService>, а вся возня за тебя уже сделана.
Я работал на проектах где были те самые настоящие синглтоны, с проверкой на нулл и локом, проблем не было вообще никаких.
Это да, речь о публичном контракте модуля. Отдельный хороший вопрос что такое модуль в мире дотнета. Вот в турбопаскале были прямо модули которые так и назывались и у которых был интерфейс в смысле публичная часть.
Но, механизм у Мартина это абстрактный класс (ну или интерфейс, в разных языках по разному). О чем он и пишет
Или более поздняя формулировка
Тут еще есть какие-то "some other kind of abstract declaration", но что это совершенно непонятно.
А дальше вот такое правило
Так что речь у него именно об абстрактных классах/интерфейсах.
Ну вот вы же в следующем предложении пишете про книгу, а я вам пишу про статью.
В какой из книг и где именно? Формулировки у него менялись (в сторону размытия), но про абстрактные интерфейсы у него как минимум в "Agile.Principles.Patterns.and.Practices", "Agile.Principles.Patterns.and.Practices in C#" и в "Clean Architecture"
Типичное утверждение про solid - за все хорошее и против всего плохого. Но все же есть вопросы. Если разбить неграмотно, то DIP не позволит? А грамотно это как именно? Таким принципам грош цена.
А про что там абстракция, если там написано abstract interface? Абстрактный класс с частичной реализацией не катит - абстракции не "должны зависит от деталей".
Пример "наивного" (в трактовке Мартина) разделения на слои это как раз отсутствие абстрактного интерфейса. А "правильное" (опять же в трактовке Мартина) разделение на слои это как раз с наличием абстрактного интерфейса.
Тут еще надо не запутаться между интерфейсом в смысле публичного контракта класса или модуля и интерфейсом в смысле абстрактного класса.
Я настоятельно рекомендую почитать оригинальную статью Мартина, там написано почему должен быть абстрактный интерфейс (абстрактный класс в плюсах), и зачем нужна инверсия владения интерфейсом. Зачем чисто плюсовые приколы в других языках вопрос риторический.
Это цитаты из книги Мартина. Если вы не в курсе, он писал на английском. А если почитаете его первоначальную статью, там даже написано почему интерфейс должен быть абстрактным классом.
Нарушаете заветы дяди Боба!
Ну пусть будет не наследование, а еще одна реализация интерфейса, это уже зависит от контекста.
Тоже самое можно сказать про огромное количество интерфейсов и их реализаций.
А еще многое зависит от проекта. Если это публичная библиотека ситуация одна. Если некий внутренний проект, ситуация другая. А у Мартина про это вообще ни словечка. ИМХО правильный подход это когда сравниваются разные способы реализации и выбирается наиболее уместный в данном конкретном случае. И в зависимости от конкретной ситуации - новый класс, новый наследник, новая реализация интерфейса, может банально новая перегрузка метода. А у Мартина просто дичь - нельзя менять бинарник!
И у вас на каждую новую фичу новая дллка или jar файл. Так вы и делаете, серьезно?
Был какой-нибудь класс Report, к нему добавили наследника AdvancedReport с новыми возможностями. В тот же самый модуль. В чем проблема?
Мартин считает иначе, цитату я привел. Я не к тому, что мнение Мартина о том как делать правильно автоматически верное. Но если мы обсуждаем принципы Мартина мы должны обсуждать его точку зрения.
Иначе получается совсем смешно. Взят банальный прием ООП, назван непонятным именем и теперь надо заплатить денег дяде Бобу чтобы он объяснил что имеется в виду.
Это "слоистая" архитектура и в ней инверсия зависимостей и/или использование абстрактного класса/интерфейса прямого отношения не имеют. Можно разделить код на слои, выстроить зависимости однонаправленно и все это без абстрактных классов.
А где-то есть переписка в открытом доступе? Мне только какие-то куски попадались.
Изначально он писал примеры на плюсах. И его первая книга была про плюсы.
Как минимум OCP и LSP это вообще не идеи дяди Боба.
Мне непонятно вот это
Как вы расширяете поведение модуля без изменения бинарников?
Сова это когда любое использование интерфейса (интерфейса в смысле абстрактного класса) называется инверсией зависимостей. Сам же Мартин пишет вот что
А это уже достаточно спорно. И если в плюсах (по крайней мере в плюсах времен Мартина) у этого есть смысл, то в тех же шарпах смысла в этом нет.
Ну наверное что-то он писал, вопрос что именно, ЕМНИП в какой-то из книг у него есть перечень где он работал и что делал.
ИМХО это просто специфический опыт. Типа рассказов про еженедельный(!) билд.
А не является ли это обычным симптомом паттернофилии, когда вместо анализа проблемы и возможных путей ее решения
лепятпытаются лепить паттерны из умной книжки? Потому что паттерны это круто. И дело вовсе не в синглтоне.Наверное это был тот случай когда синглтон был не на своем месте.
Это если хорошенько натянуть сову на глобус. Что-то типа - используйте интерфейсы/абстрактные классы там где надо, а где не надо, не используйте. Хороший принцип? Хороший. Знание настолько общего принципа как-то помогает писать хороший код? Нет.
Это не говоря о том, что изначально Мартин писал исключительно про плюсы и сам писал что в других языках проблемы нет. В интернетах есть его оригинальная статья, абзац " Separating Interface from Implementation in C++".
Вот! Когда Мартин еще сам писал код, а не учил этому других, многое было иначе. В частности систем контроля версий в современном понимании этого слова не было. Системы были, конечно, но были они откровенно убогие по нынешним меркам. Да и даже убогие использовались далеко не везде. А в такой ситуации одновременная работа с файлом (классом, модулем), когда несколько человек одновременно вносят правки превращается в мучение. И подход - разбейте класс/модуль на части чтобы исключить одновременные правки это реально работающая вещь.
Что касается OCP, то придуман он был Мейером и у него явно сказано что это про организацию работы. Мартин же довел принцип до абсурда - нельзя чтобы бинарники менялись при добавлении нового функционала.
Потому что мало где нужен, да и сейчас повсеместно DI контейнеры, пишешь что-то вроде container.AddSingleton<MySingletonService>, а вся возня за тебя уже сделана.
Я работал на проектах где были те самые настоящие синглтоны, с проверкой на нулл и локом, проблем не было вообще никаких.
Он самый.