Инъекция зависимости: Должен ли я создать класс Car, содержащий все его части?

10

У меня есть много автомобилей в моем приложении C ++, все они содержатся в RaceTrack.

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

Я прочитал много SO вопросов о DI и книге Марка Симанна, и похоже, что я не должен определять класс Car только для хранения автомобильных деталей, потому что все автомобильные детали будут зависеть друг от друга, и этот суппорт - это автомобиль. Я прав?

Так что, когда я помещу все свои гоночные автомобили в RaceTrack, я не буду иметь никаких автомобильных сущностей, но будет много деталей машин, зависящих друг от друга, участвующих в гонке на треке ?? Я прав?

РЕДАКТИРОВАТЬ:

Целую вечность я программировал автомобильные классы, потому что для меня было очевидно, что мне нужен автомобильный класс, если я программирую автомобильную логику. Но с DI это не так очевидно для меня. Все еще задаетесь вопросом, является ли идиоматическим для DI не создавать класс Car, если у меня нет определенной роли для него?

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

Антон Петров
источник

Ответы:

24

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

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

Применение

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

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

Внедрение зависимости

Инъекция зависимости не имеет к этому никакого отношения. Когда я веду машину, я не думаю о том, как она была построена. Пока это работает, мне все равно, как они соединяют это.

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

Я действительно придерживаюсь принципа: отделить использование от строительства.

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

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

Вот что такое DI. Конечно, это не так просто, как newпридумать что-то, как только вы поймете, что хотите этого. Вместо этого вы должны написать отдельный код, который знает, как построить свой автомобиль. Но это, конечно, помогает изменить вещи позже. А это значит, что вам не нужно тащить с собой автосборочный завод по трассе.

строительство

Что-то где-то должно знать, шины ли Goodyear. Где тогда поставить код конструкции автомобиля? Если не машина, то яма экипажа? Трек? Нет. У всех есть код поведения. Код, который должен выполнить во время гонки. Построение автомобиля должно происходить до начала гонки в месте, удаленном от кодекса поведения. Марк Симанс назвал это место Композиционным Корнем . Большинство людей называют это главным.

Это простой шаблон. В основном, создайте граф объектов, затем вызовите один поведенческий метод для одного объекта в графе объектов. Вот и все. Это ЕДИНСТВЕННОЕ место строительства и поведения должны быть вместе.

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

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

DI инструменты

Инструмент DI покупает у вас детали конструкции, переведенные на другой язык (xml, json и т. Д.), Который обеспечивает разделение между конструкцией и поведением. Если вы не доверяете своим коллегам-программистам не использовать их, newкогда они этого не делают, это может быть привлекательным.

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

Принципы дизайна

Целую вечность я программировал автомобильные классы, потому что для меня было очевидно, что мне нужен автомобильный класс, если я программирую автомобильную логику. Но с DI это не так очевидно для меня. Все еще задаетесь вопросом, является ли идиоматическим для DI не создавать класс Car, если у меня нет определенной роли для него?

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

Один из самых фундаментальных вопросов в разработке программного обеспечения - «что знает о чем?» Это главное, что показывает диаграмма UML. Когда вы открываете что-то новое, вы проходите мимо его интерфейса с конкретной вещью, с которой вы сейчас связаны. Теперь автомобиль должен знать, что шины Goodyear. Что-то вроде отстой, если Мишлен хочет спонсировать тебя.

Избегать этого называется следование принципу инверсии зависимостей . Формально модуль высокого уровня (например, класс автомобиля) не должен напрямую зависеть от модулей низкого уровня (например, класс GoodyearTire). Это должно зависеть от абстракции (например, интерфейс Tire).

Способ избежать этого называется инверсией контроля . Здесь акцент делается на изменении потока управления. Шины двигают автомобиль или автомобиль движет шины? Думая об этом правильный путь позволяет нам не статически соединять автомобиль и шины вместе. DI является одним из конкретных способов следовать Inversion of Control.

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

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

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

candied_orange
источник
Было бы здорово, если бы вы не использовали метафору машины и экипажа для вопроса, который использует их для представления кода. Но все же хороший ответ.
С. Тарык Четин
6
И я всегда думал, что Инверсия Контроля - это когда ты поворачиваешь влево, а машина поворачивает направо ...
mkrieger1
CandiedOrange, значит, нет класса Car, если у него нет осмысленного интерфейса и четко определенной ответственности? И нет Car.h в моем проекте. И коллега изучает мой код и задается вопросом: это интернет-магазин автомобильных запчастей или приложение для управления гаражом? :)
Антон Петров
@AntonPetrov "если он выглядит как утка и крякает как утка, но нуждается в батарейках, значит, у вас неправильная абстракция". Хорошие имена очень важны, и о них трудно думать, но их следует изменить, чтобы отразить четко определенную ответственность и содержательный интерфейс, чтобы они не стали неожиданностью. Если я читаю название, то смотрю на интерфейс и думаю, что «это почти то, что я ожидал», тогда у вас хорошее имя.
candied_orange
15

Похоже, я не должен определять класс Car только для хранения частей автомобиля, потому что все части автомобиля будут зависеть друг от друга, и этот суп из частей является автомобилем. Я прав?

Нет.

Точка внедрения зависимости вовсе не препятствует зависимостям. Наоборот.

Зависимость от инъекции около вопрос: где же автомобиль получить его части от ? Где и как они созданы, и как автомобиль получает ссылки на них?

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

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

Майкл Боргвардт
источник
1
Наконец, вменяемое объяснение dependency injectionконцепции.
анатолий техтоник
1
Я бы сказал, что более распространенная система для «наивного» подхода заключается не в том, что автомобиль будет называть новым, а в том, что пользователь автомобильного класса назначит свои части своим свойствам, и в то же время класс автомобиля будет неопределенное состояние. DI предоставляет средства для механического обеспечения валидности класса.
whatsisname
2
@whatsisname Я могу легко использовать DI для создания недопустимых и наполовину инициализированных объектов. Валидация не является обязанностью DI. И не является неизменным. Я могу взять на себя работу по созданию действительных и неизменных объектов. У DI нет способа обеспечить, чтобы это были единственные способы, которыми эти объекты создаются и используются. Объекты должны обеспечивать это.
candied_orange
@CandiedOrange: так что вы можете легко использовать любую технику, чтобы сделать что-то наполовину, ну и что? Требовать зависимостей у конструктора и делать их неизменяемыми - это не серебряная пуля, но, тем не менее, это очень полезный способ предотвращения многих распространенных плохих ситуаций.
whatsisname
0

Так что, когда я помещу все свои гоночные автомобили в RaceTrack, я не буду иметь никаких автомобильных сущностей, но будет много деталей машин, зависящих друг от друга, участвующих в гонке на треке ?? Я прав?

Хорошо. Да и нет. Автомобильный объект все еще действует. И автомобиль больше, чем сумма его частей. Тебе тоже нужен водитель (пока). Во многих случаях у гоночных автомобилей также есть названия, не говоря уже о марке и модели автомобиля.

Да, автомобили - это, в основном, контейнер деталей, но именно эта упаковка и совместная работа деталей составляют нечто большее: автомобиль.

Так что на самом деле нет. Автомобиль - это не просто сумка из металла и пластика. Это машина.

Внедрение зависимости не является чрезмерным в этом случае. Что-то еще должно построить машину, которая будет классом фабрики или объектом Builder. Автомобиль не должен знать, как построить себя.

Грег Бургардт
источник
2
Вы можете иметь DI без фабрик или объектов застройщика. Параметры простого конструктора являются допустимым методом, который работает для многих обстоятельств.
whatsisname