Есть много причин, почему глобалы злы в ООП.
Если число или размер объектов, нуждающихся в совместном использовании, слишком велик, чтобы их можно было эффективно передать в параметрах функции, обычно все рекомендуют внедрение зависимостей вместо глобального объекта.
Однако в случае, когда почти каждый должен знать об определенной структуре данных, почему внедрение зависимостей лучше, чем глобальный объект?
Пример (упрощенный, чтобы показать суть в целом, не углубляясь в конкретное приложение)
Существует ряд виртуальных транспортных средств, которые имеют огромное количество свойств и состояний, от типа, имени, цвета, скорости, положения и т. Д. Ряд пользователей может управлять ими дистанционно, а также огромное количество событий (как инициируется и автоматически) может изменить многие их состояния или свойства.
Наивным решением было бы просто создать из них глобальный контейнер, например
vector<Vehicle> vehicles;
к которому можно получить доступ откуда угодно.
Более дружественным к ООП решением было бы иметь контейнер, являющийся членом класса, который обрабатывает основной цикл событий, и создавать его экземпляры в своем конструкторе. Каждый класс, который нуждается в нем и является членом основного потока, получит доступ к контейнеру через указатель в своем конструкторе. Например, если внешнее сообщение поступает через сетевое соединение, класс (по одному на каждое соединение), обрабатывающий синтаксический анализ, вступит во владение, и парсер получит доступ к контейнеру через указатель или ссылку. Теперь, если проанализированное сообщение приводит к изменению элемента контейнера или требует каких-либо данных из него для выполнения действия, оно может обрабатываться без необходимости бросать тысячи переменных через сигналы и слоты (или, что еще хуже, сохранение их в парсере для последующего извлечения тем, кто вызвал парсер). Конечно, все классы, которые получают доступ к контейнеру через внедрение зависимостей, являются частью одного и того же потока. Различные потоки не будут напрямую обращаться к нему, но будут выполнять свою работу, а затем отправлять сигналы в основной поток, а слоты в основном потоке будут обновлять контейнер.
Однако, если большинство классов получат доступ к контейнеру, что действительно отличает его от глобального? Если такому классу нужны данные в контейнере, не является ли «путь внедрения зависимостей» просто замаскированным глобальным?
Одним из ответов будет безопасность потоков: даже если я позабочусь о том, чтобы не злоупотреблять глобальным контейнером, возможно, другой разработчик в будущем, под давлением близкого срока, все же будет использовать глобальный контейнер в другом потоке, не заботясь обо всех случаи столкновения. Однако даже в случае внедрения зависимости можно указать указатель на кого-то, работающего в другом потоке, что приводит к тем же проблемам.
Ответы:
Инъекция зависимости - лучшая вещь после нарезанного хлеба , в то время как глобальные объекты были известны на протяжении десятилетий как источник всего зла , так что это довольно интересный вопрос.
Точка внедрения зависимости заключается не просто в том, чтобы гарантировать, что каждый субъект, которому нужен какой-либо ресурс, может иметь его, потому что, очевидно, если вы сделаете все ресурсы глобальными, то каждый субъект будет иметь доступ к каждому ресурсу, проблема решена, верно?
Точка внедрения зависимости:
Тот факт, что в вашей конкретной конфигурации всем действующим лицам необходим доступ к одному и тому же экземпляру ресурса, не имеет значения. Поверьте мне, однажды у вас возникнет необходимость перенастроить вещи так, чтобы актеры имели доступ к различным экземплярам ресурса, и тогда вы поймете, что нарисовали себя в углу. Некоторые ответы уже указали на такую конфигурацию: тестирование .
Другой пример: предположим, что вы разбили свое приложение на клиент-сервер. Все участники на клиенте используют один и тот же набор центральных ресурсов на клиенте, и все участники на сервере используют один и тот же набор центральных ресурсов на сервере. Теперь предположим, что однажды вы решите создать «автономную» версию своего клиент-серверного приложения, где и клиент, и сервер упакованы в один исполняемый файл и работают на одной виртуальной машине. (Или среда выполнения, в зависимости от вашего языка.)
Если вы используете внедрение зависимостей, вы можете легко убедиться, что всем действующим лицам предоставлены экземпляры ресурсов клиента для работы, в то время как все действующие лица сервера получают экземпляры ресурса сервера.
Если вы не используете внедрение зависимостей, вам совершенно не повезло, поскольку в одной виртуальной машине может существовать только один глобальный экземпляр каждого ресурса.
Затем вы должны подумать: действительно ли всем актерам нужен доступ к этому ресурсу? действительно?
Возможно, вы совершили ошибку, превратив этот ресурс в объект бога (так что, конечно, каждый должен иметь к нему доступ), или, возможно, вы сильно переоцениваете количество участников в вашем проекте, которым действительно нужен доступ к этому ресурсу. ,
Благодаря глобальным переменным каждая строка исходного кода во всем приложении имеет доступ к каждому глобальному ресурсу. С внедрением зависимостей каждый экземпляр ресурса виден только тем акторам, которым он действительно нужен. Если они одинаковы (актеры, которым нужен конкретный ресурс, составляют 100% строк исходного кода в вашем проекте), значит, вы допустили ошибку в своем проекте. Так что либо
Преобразуйте этот большой большой огромный ресурс бога в меньшие подресурсы, чтобы разные актеры нуждались в доступе к разным его частям, но редко актеру нужны все его части, или
Рефакторинг ваших актеров, чтобы они в свою очередь принимали в качестве параметров только те подмножества проблемы, над которыми им нужно работать, чтобы им не приходилось постоянно консультироваться с каким-то большим огромным центральным ресурсом.
источник
Это сомнительное утверждение. Ссылка используется в качестве доказательства относится к государственному - изменяемым глобал. Они явно злые. Только для чтения глобалы являются только константами. Константы относительно вменяемые. Я имею в виду, вы не собираетесь вводить значение числа Пи во все ваши классы, не так ли?
Нет, они не
Если ваши функции / классы имеют слишком много зависимостей, люди рекомендуют остановиться и посмотреть, почему ваш дизайн такой плохой. Наличие множества зависимостей является признаком того, что ваш класс / функция, вероятно, делает слишком много вещей и / или у вас недостаточно абстракции.
Это дизайнерский кошмар. У вас есть куча вещей, которые можно использовать / злоупотреблять без какой-либо проверки, без каких-либо ограничений параллелизма - это просто соединение во всем.
Да, привязка к общим данным везде не слишком отличается от глобальной, и, как таковая, все еще плоха.
Плюс в том, что вы отделяете потребителей от самой глобальной переменной. Это дает вам некоторую гибкость в том, как создается экземпляр. Это также не заставляет вас иметь только один экземпляр. Вы могли бы сделать разные, которые будут переданы вашим различным потребителям. Затем вы также можете создать различные реализации вашего интерфейса для каждого потребителя, если вам нужно.
И из-за изменений, которые неизбежно сопровождают меняющиеся требования к программному обеспечению, такой базовый уровень гибкости является жизненно важным .
источник
foo
s»?Есть три основные причины, которые вы должны рассмотреть.
Итак, подведем итог: DI это не цель, это всего лишь инструмент, который позволяет достичь цели. Если вы используете его не так (как вы, кажется, делаете в своем примере), вы не приблизитесь к цели, нет магического свойства DI, которое сделает плохой дизайн хорошим.
источник
На самом деле речь идет о тестируемости - обычно глобальный объект очень удобен в обслуживании и легко читается / понимается (конечно, если вы знаете его там), но это постоянное приспособление, и когда вы разбиваете свои классы на изолированные тесты, вы обнаруживаете, что ваш глобальный объект застрял, говоря «что обо мне», и поэтому ваши изолированные тесты требуют, чтобы глобальный объект был включен в них. Не помогает, что большинство тестовых сред не поддерживают этот сценарий.
Так что да, это все еще глобальный. Фундаментальная причина его наличия не изменилась, а только то, как к нему обращаются.
Что касается инициализации, это все еще проблема - вам все равно нужно настроить конфигурацию, чтобы ваши тесты могли работать, чтобы вы ничего не получили там. Я полагаю, вы могли бы разделить большой зависимый объект на множество меньших и передать соответствующий классу, который в них нуждается, но вы, вероятно, получаете еще большую сложность, чтобы перевесить любые преимущества.
Независимо от всего этого, например, «хвост виляет собакой», необходимо проверить, как определяется кодовая база (аналогичные проблемы возникают с такими элементами, как статические классы, например, текущая дата / время).
Лично я предпочитаю помещать все свои глобальные данные в глобальный объект (или несколько), но предоставляю средства доступа, чтобы получить биты, которые нужны моим классам. Затем я могу посмеяться над тем, чтобы вернуть данные, которые мне нужны, когда дело доходит до тестирования, без дополнительной сложности DI.
источник
В дополнение к ответам уже у вас есть,
Одной из характеристик ООП программирования является сохранение только тех свойств, которые вам действительно нужны для конкретного объекта,
СЕЙЧАС, если у вашего объекта слишком много свойств - вы должны разбить ваш объект на дочерние объекты.
редактировать
Уже упоминалось, почему глобальные объекты плохие, я бы добавил один пример.
Вам необходимо выполнить модульное тестирование кода GUI формы Windows. Если вы используете global, вам нужно будет создать все кнопки и их события, например, чтобы создать подходящий тестовый пример ...
источник