Нарушает ли композиция букву D в SOLID?

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

Означает ли это, что композиция запрещена? Кроме того, агрегация аналогична DIP?

Состав:

    class HPDesktop {
        private BluetoothMouse bluetoothMouse;
        private BluetoothKeyboard bluetoothKeyboard;
    
        public HPDesktop(){
            bluetoothMouse = new BluetoothMouse();
            bluetoothKeyboard = new BluetoothKeyboard();
        } 
     }

ДИП: (Похоже, это совокупность)

class HPDesktop {
    private Mouse mouse;
    private Keyboard keyboard;

    public HPDesktop(Mouse mouse, Keyboard keyboard){
        this.mouse = mouse;
        this.keyboard = keyboard;
    }
}
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
0
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Инверсия зависимостей — это концепция, согласно которой модули высокого уровня не должны зависеть от деталей модуля низкого уровня. Такое «раскрытие» деталей модуля низкого уровня называется «дырявой абстракцией».

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

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

Как это делается? Что ж, интерфейсы предоставляют средства инвертирования направления зависимости. Я напрямую завишу (в фасаде) от интерфейса, который определяет мои уровни журнала для уровней журнала Log4j, и какой-то модуль предоставляет их (напрямую в зависимости от того же интерфейса.

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

Чтобы еще раз подчеркнуть эту мысль, клиент Фасада, использующий прямые зависимости, должен будет

import facade;
import log4j;

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

import facade;

и позже я могу подключить другой jar, чтобы обеспечить реализацию log level interface, а это означает, что код моего клиента не должен меняться, когда я заменяю серверных поставщиков уровня журнала.

Я не уверен, как именно применить этот ответ к вопросу. Является ли здесь ответом, что ни один из примеров OP не нарушает DIP? Или ни один из примеров не имеет отношения к ДИП? Говорит ли этот ответ, что DIP является синонимом утверждений import? Разве транзитивные зависимости не входят в компетенцию DIP?

jaco0646 12.06.2024 14:47

Это во многом зависит от того, где определены (предполагаемые) интерфейсы Mouse и Keyboard и как получены их реализующие классы. Когда оно определено способами, которые не создают прямой зависимости от обернутых/скрытых модулей реализации, скажем, путем определения их в модулях фасада и использования фабрик или сборщиков на основе строк, тогда используется DIP. Помните, используйте DIP только для тех предметов, которые вы планируете (или подозреваете) заменить. Материалы внутри модулей не являются межмодульными зависимостями. По сути, агрегация и DIP ортогональны (одно не влияет на другое).

Edwin Buck 12.06.2024 14:53

Действительно хороший способ сделать это в Java - использовать «инфраструктуру поставщика услуг», в которой вы в основном используете интерфейс, а затем сканируете «загрузчик сервисов» на предмет реализаций интерфейса в других модулях. baeldung.com/java-spi представляет собой нежное введение. По сути, вы используете функцию загрузчика модулей для чтения информации манифеста, которая выполнит автоматическое подключение за вас. Spring тоже может это сделать, но гораздо более сложным способом, поскольку он был создан первым (и, следовательно, немного менее усовершенствован).

Edwin Buck 12.06.2024 14:57

Не хочу отвлекать внимание, но это похоже на анти-паттерн Service Locator. Я согласен, что агрегация ортогональна DIP. Ошибка в ОП заключается в объединении агрегации с абстракцией (этого можно достичь, используя те же конкретные зависимости, что и в примере композиции). Пример композиции сложнее: трудно понять, как прямое создание экземпляров может удовлетворить DIP. Я думаю, что ошибка заключается в объединении композиции с прямым созданием экземпляра. Можете ли вы дать определение DIP, не прибегая к примерам?

jaco0646 12.06.2024 15:22

Вы обнаружите, что (особенно в 2010-х годах) многие желающие стать лекторами открыли для себя искусство привлечения заголовков. Я не верю, что локатор сервисов — это антишаблон, а книга, которую написал ваш автор, была посвящена .Net, которая всегда была на шаг позади в изучении идей Java. В его книге говорится о «проводящем клиенте», который представляет собой класс «БОГ», который объединяет вашу программу. Как только вы это сделаете, для переконфигурации вашей программы (скажем, для тестирования) вам понадобится новый класс «БОГ», чтобы сделать новый проводку, и в конечном итоге, когда вы пишете реконфигурируемый, он становится (в основном) локатором сервисов.

Edwin Buck 12.06.2024 15:38

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

CrystalSapphire 12.06.2024 15:39

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

Edwin Buck 12.06.2024 15:40

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

Edwin Buck 12.06.2024 15:46

@CrystalSapphire Хотя агрегация связана с связью между объектами. По сути, это шаблоны, которые определенные объекты имеют друг с другом, когда эти группы объектов должны работать как единое целое. Иногда у этой группы были разные жизненные циклы, иногда у них были общие жизненные циклы, иногда у них были разные владельцы, иногда они были общими. Например, объект HashTable имеет HashEntries, которые ссылаются на записи объекта. Ни HashTable, ни HashEntry не могут быть полезны друг другу, поэтому они являются агрегатами функциональности HashTable.

Edwin Buck 12.06.2024 15:48

@CrystalSapphire Идея агрегата возникла из-за выбора представлять деревья как агрегаты объектов TreeNode. По сути, агрегация — это группа связанных объектов, которые для работы должны иметь определенную структуру, и обычно эта структура открыта, в то время как композиция — это форма агрегации. Композиция обычно фокусируется на том, чтобы не раскрывать детали шаблона объекта, а скрывать их. Таким образом, в сценарии дерева TreeNode является совокупностью дерева и (что сбивает с толку в некоторых библиотеках), возможно, тоже является деревом само по себе!

Edwin Buck 12.06.2024 15:54

@jaco0646 Имейте в виду, что Марк Симанн в своей книге о внедрении зависимостей говорит, что вызов «ServiceLocator» и антишаблона является спорным, основываясь на этом сообщении Дэниела Каззулино asp-blogs.azurewebsites.net/cazzu/… что по сути говорит, что DI плох, потому что вы не кодируете свою зависимость в исходном коде, а это именно то, чего DI пытается избежать. Я бы не сказал, что это сейчас спорно, но я бы сказал, что ранний Spring с этим справлялся плохо, а JVM намного лучше.

Edwin Buck 12.06.2024 16:04

Другие вопросы по теме