инвертировать управление что значит
Инверсия управления/Inversion of Control
Инверсия управления является распространенным явлением, с которым вы столкнетесь при использовании фреймворков. И действительно, она часто рассматривается как определяющая характеристика фреймворка.
Давайте рассмотрим простой пример. Представьте себе, что я пишу программу, которая получает некоторую информацию от пользователя с помощью командной строки. Я мог бы сделать это как-то так:
В этой ситуации мой код управляет исполнением: он решает, когда задавать вопросы, когда считывать ответы, а когда обрабатывать результаты.
Однако если бы я использовал оконную систему для чего-то похожего, я написал бы что-то, что работает с окном:
Теперь между этими двумя программами большая разница в потоке управления — в частности, в управлении временем, когда вызываются методы process_name и process_quest. В примере с коммандной строкой я контролирую, когда эти методы вызываются, но в примере с оконным приложением нет. Вместо этого я передаю контроль оконной системе (команда Tk.mainloop). Далее она решает, когда вызвать мои методы, основываясь на связях, которые я настроил при создании формы. Управление инвертировано — управляют мной, а не я управляю фреймворком. Это явление и называется инверсией управления (также известно как Принцип Голливуда — «Не звони нам, мы сами позвоним тебе» — Hollywood Principle — «Don’t call us, we’ll call you»).
Одной важной характеристикой фреймворка является то, что методы, определенные пользователем для адаптации фреймворка под свои нужды, будут чаще всего вызываться внутри самого же фреймворка, а не из кода приложения пользователя. Фреймворк часто играет роль главной программы в координации и последовательности действий приложения. Такая инверсия управления дает фреймворку возможность служить расширяемым скелетом приложения. Методы, предоставляемые пользователем, адаптируют общие алгоритмы, определенные фреймворком, под определенное приложение.
Ральф Джонсон и Брайан Фут.
Инверсия управления является ключевой частью того, что различает фреймворк и библиотеку. Библиотека это по существу набор функций, которые вы можете вызывать, в наши дни они организованы в классы. Каждый вызов выполняет некоторую работу и возвращает управление обратно пользователю.
Фреймворк воплощает в себе некоторый абстрактный дизайн со встроенным поведением. Для того, чтобы использовать его, вы должны добавить свой код в различных местах фреймворка, либо через наследование, либо подключив свой собственный класс. Код фреймворка впоследствии будет вызывать ваш код.
Существуют различные способы подключить ваш код для его дальнейшего вызова. В предыдущем примере на ruby, мы вызываем метод bind текстового поля, который принимает имя события и замыкание в качестве аргументов. Всякий раз, когда текстовое поле узнает о событии, оно вызывает наш код из замыкания. Использование таких замыканий очень удобно, но многие языки не поддерживают их.
Рассмотренные выше подходы (они одинаковые) хорошо подходят в единичных случаях, но иногда вам может понадобиться объединить некоторое количество вызовов в единый блок расширения. В этом случае фреймворк может определить интерфейс, который ваш код должен будет реализовать для соответствующих вызовов.
EJB-компоненты являются хорошим примером такого стиля инверсии управления. При разработке сессионного компонента(session bean), вы можете реализовать различные методы, которые вызываются EJB-контейнером в различных точках/состояниях жизненного цикла. Например, у интерфейса SessionBean есть методы ejbRemove, ejbPassivate (сохранен во вторичное хранилище) и ejbActivate (восстановлен из пассивного состояния). Вы не можете управлять вызовом этих методов, только тем, что они делают. Контейнер вызывает нас, а не мы вызываем его.
Примечание перевода, пример:
Это сложные случаи инверсии управления, но вы столкнетесь с этим в гораздо более простых ситуациях. Шаблонный метод является хорошим примером: супер-класс определяет поток управления, субклассы наследуются от него переопределяя методы или реализуя абстрактные методы. Например, в JUnit, код фреймворка вызывает методы setUp и tearDown для вас, чтобы создавать и очищать ваш тест. Происходит вызов, ваш код реагирует — это снова инверсия управления.
Примечание перевода, пример:
В наши дни, в связи с ростом количества IoC-контейнеров, существует некоторая путаница со смыслом инверсии управления. Некоторые люди путают общий принцип с конкретными стилями инверсии управления (такими как внедрение зависимостей), которые эти контейнеры используют. Все это немного запутанно (и иронично), так как IoC-контейнеры, как правило, рассматриваются в качестве конкурента EJB, но EJB использует инверсию управления.
Этимология: Насколько я могу судить, термин инверсии управления впервые появился на свет в работе Джонсона и Фута Designing Reusable Classes, опубликованной в журнале Object-Oriented Programming в 1988 году. Работа является одной из тех, что в возрасте хороши — ее можно прочитать даже спустя пятнадцать лет. Они считают, что они взяли этот термин откуда-то еще, но не могут вспомнить откуда. Затем термин втерся в объектно-ориентированное сообщество и вновь появился в книге Gang of Four. Более красивый синоним «Принцип Голливуда», кажется, берет начало в работе Ричарда Свита в Mesa в 1983 году. В списке целей разработки он пишет: Не звони нам, мы сами позвоним тебе (Закон Голливуда): инструмент должен организовать Тахо, чтобы предупредить его, когда пользователь захочет передать какое-то событие инструменту, вместо того, чтобы принимать модель «запросить у пользователя команду и выполнить ее». Джон Влиссидес пишет колонку о C++, которая несет в себе хорошее объяснение концепции под названием «Принцип Голливуда». (Спасибо Брайану Футу и Ральфу Джонсону за помощь с этимологией).
Для чего в играх нужна инверсия по оси Y
Все знают, что в настройках практически любой игры есть инверсия. Когда мы тянем контроллер на себя и обзор камеры уходит вверх. Зачем ее добавляют в игры? Многие могут посчитать это каким-то извращением, однако, когда дело касается «леталок», инверсия кажется нам вполне логичной. Почему происходит такая избирательность?
Но как объяснить феномен, когда инверсию по оси Y используют в шутерах от первого и третьего лица? Как ни странно, здесь играют привычки управления джойстиком с детства, а также психологический фактор восприятия. Картинка ниже, которую затаскали по всей Сети, прекрасно объясняет, почему игрок руководствуется таким выбором.
Эту картинку наверняка видели все, кто интересовался инверсией. Перевел ее на русский, чтобы было понятней, хотя тут и так все наглядно)
То есть, в данном случае, игрок не сливается воедино с персонажем, а становится этаким «кукловодом». Попробуйте включить инверсию по оси Y в какой-нибудь игре от третьего лица и у вас появится дискомфорт от того, что все поменялось(мышечная память привыкла к стандартам), но при этом мозг не даст сигналов, что это плохо/пиши пропало, наоборот, в глубине души, какая-то частица даже скажет, что это вполне логично.
А как же с шутерами от первого лица? Мы же смотрим на все глазами героя, вживаемся в него, и уж точно мы не будем «марионеткой». Какой мотив для инверсии здесь? Представьте камеру на штативе. Когда мы направляем ее вверх, мы тянем ее назад, и наоборот. То же самое происходит с нашей головой, когда мы смотрим наверх, затылок уходит вниз. Мы смотрим себе под ноги и наклоняемся вперед. Эти действия кто-то тоже может воплотить с мышью. Но на это есть контраргумент.
А это уже взгляд более «традиционного» игрока.
Инверторный привод в бытовой технике: что это, плюсы и минусы инверторного управления
Инверторное управление обеспечивает более надежную и экономичную работу бытовой техники. Устройства с инверторным приводом демонстрируют меньшую расточительность в энергопотреблении, с высокой точностью поддерживают требуемые обороты мотора, отличаются малошумностью и увеличенным эксплуатационным ресурсом. Применяются инверторы в холодильниках, стиральных машинах, посудомойках, кондиционерах и даже в обогревателях. Давайте познакомимся с технологией ближе.
Инверторные системы позволяют держать сердце бытовой техники постоянно «бьющимся» и плавно регулировать мощность, подбирая ее под конкретные нужды. Как минимум, это уменьшает преждевременный износ узлов. Для экономии энергии инверторные моторы не отключаются полностью — они снижают обороты, когда в работе «на полную катушку» нет необходимости. Плавные переходы с высоких на низкие обороты менее вредны для бытового прибора, чем постоянные включения/отключения.
В основе инверторной технологии лежит принцип двойного преобразования напряжения. Сперва синусоидальное напряжение на входе преобразуется в постоянное, фильтруется и сглаживается. На втором этапе из него формируются положительные и отрицательные управляющие импульсы. С их помощью создается переменное напряжение требуемой величины и частоты на выходе преобразователя, которое затем и подается на двигатель. Сам принцип работы двигателя от наличия инвертора не зависит, но этот дополнительный блочок дает возможность управлять работой электромотора в широких границах.
Многие именитые производители бытовой техники не стесняются давать на инверторные приводы увеличенную гарантию (10 лет и больше). |
Зачастую инверторы включаются в схемы с асинхронными двигателями. Они решают проблемы с высокими пусковыми токами и перегрузками, а также позволяют изменять скорость вращения ротора электромотора от десятков оборотов в минуту до нескольких тысяч. Наиболее важное преимущество подобного сочетания — отсутствие в конструкции мотора уязвимого щеточного узла, который является визитной карточкой электродвижков коллекторного типа. Со временем токоподводящие щетки в них изнашиваются и нуждаются в замене. Главные действующие лица моторов с инверторным приводом: вращающийся ротор с постоянными магнитами и неподвижный статор с катушками индуктивности. Впрочем, схемы управления, называемые инвертором, в разных устройствах могут быть реализованы совершенно по-разному.
Глубоко в дебри технической стороны вопроса мы лезть не будем. Акцент лучше поставить на преимуществах и недостатках инверторной технологии, а также особенностях ее применяемости в конкретных группах бытовой техники.
Итак, приступим ко всестороннему изучению качеств инверторов с холодильного оборудования:
Холодильники и морозилки
Холодильники и морозильные камеры с традиционным компрессором линейной конструкции систематически отключаются по достижению оптимальной температуры в камере. Точность ее поддержания варьируется в пределах 3-5 °С (зависит от конкретной модели). При каждом последующем включении мотор холодильника испытывает повышенные пусковые нагрузки, его элементы разогреваются, что провоцирует преждевременный выход узлов из строя, а домочадцы отчетливо слышат гудение во время работы компрессора.
Инверторный компрессор берет разгон с малых оборотов, наращивая силы постепенно. Первым делом он охлаждает воздух в камере до нужного значения температуры, после чего сбавляет темп на ее поддержание. Движок то работает почти неслышно на минимальных оборотах, то немного прибавляет мощности и его размеренное «урчание» можно услышать. Такой режим наиболее благоприятен для компрессора, поскольку он работает без пусковых перегрузок и за счет их же отсутствия потребляет меньше электроэнергии.
Наглядные преимущества использования инверторного компрессора в гипотетической модели холодильника. |
Плюсы инверторных компрессоров в холодильниках и морозилках:
Как использовать Инверсию Управления в JavaScript и в Reactjs для упрощения работы с кодом
Инверсия Управления это довольно простой для понимания принцип программирования, который, при этом, может заметно улучшить ваш код. В данной статье будет показано как применять Инверсию Управления в JavaScript и в Reactjs.
Если вы уже писали код который используется больше чем в одном месте, то вам знакома такая ситуация:
Что именно делает код кошмаром для использования и поддержки? Есть несколько аспектов, которые могут сделать ваш код проблемным:
В итоге все страдают. Стоит заметить что реализация конечной программы является важнейшей частью разработки. Но было бы отлично если бы мы больше думали о реализации наших абстракций (читайте про «AHA programming»). Существует ли способ, который позволит нам уменьшить проблемы с повторно используемым кодом, и, при этом, все еще пожинать преимущества использования абстракций?
Инверсия управления
Инверсия управления это принцип который действительно упрощает создание и использование абстракций. Вот что говорит об этом Википедия:
… в традиционном программировании пользовательский код, который выражает назначение программы, вызывает в многократно используемых библиотеках для решения общих задач, но с инверсией управления это среда, которая вызывает пользовательский или специфичный для задачи код,
Думайте об этом так: «Сократите функционал вашей абстракции, и сделайте так чтобы ваши пользователи сами могли реализовывать нужный им функционал». Это может показаться полным абсурдом, мы же ведь для того и используем абстракции чтобы спрятать сложные и повторяющиеся задачи, и тем самым сделать наш код более «чистым» и «аккуратным». Но, как мы уже убедились выше, традиционные абстракции не всегда упрощают код.
Что такое Инверсия Управления в коде?
Для начала, вот очень надуманный пример:
Теперь давайте разыграем типичный «жизненный цикл абстракции», добавляя в эту абстракцию новые варианты использования и «бездумно улучшая» его для поддержания этих новых вариантов использования:
Итак, наша программа работает всего с шестью вариантами использования, но фактически мы поддерживаем любую возможную комбинацию функций, и таких комбинаций насчитывается аж 25 (если я правильно посчитал).
В целом, это довольно простая абстракция. Но ее можно упростить. Часто бывает так, что абстракцию, в которую добавляли новый функционал, можно было бы сильно упростить для тех вариантов использования которые она фактически и поддерживает. К сожалению, как только абстракция начинает что-то поддерживать (например, выполнение < filterZero: true, filterUndefined: false >), мы боимся удалять эту функциональность из-за того что это может сломать код который на нее полагается.
Мы даже пишем тесты для вариантов использования, которых у нас, на самом деле нету, просто потому что наша абстракция поддерживает эти сценарии, и нам «может» понадобиться сделать это в будущем. А когда те или иные варианты использования становятся не нужными для нас, мы не удаляем их поддержку, так как просто забываем об этом, или думаем что это может пригодиться нам в будущем, или просто боимся чего нибудь сломать.
Окей, давайте теперь напишем более продуманную абстракцию к этой функции и применим метод инверсии управления для поддержки всех нужных нам вариантов использования:
Отлично! Вышло намного проще. Мы только что перевернули управление функцией, передав ответственность за принятие решения о том, какой элемент попадает в новый массив, с функции filter на функцию, вызывающую функцию фильтра. Заметьте, что функция filter все еще является полезной абстракцией сама по себе, но теперь она гораздо более гибкая.
Но была ли предыдущая версия этой абстракции настолько уж плохой? Возможно, нет. Но так как мы перевернули управление, теперь мы можем поддерживать гораздо более уникальные варианты использования:
Только представьте если бы вам нужно было добавить поддержку для этого варианта использования, не применяя инверсию управления? Да это было бы просто нелепо.
Плохой API?
Одна из самых распространенных жалоб, которую я слышу от людей, касательно API-интерфейсов в которых применяется инверсия управления это: «Да, но теперь этим сложнее пользоваться, чем раньше». Возьмите этот пример:
Да, один из вариантов явно проще в использовании, чем другой. Но одним из преимуществ инверсии управления является то что вы можете использовать API в котором применяется инверсия управления для повторной реализации вашего старого API. Обычно это довольно просто. К примеру:
Круто, да? Таким образом мы можем создавать абстракции поверх API в котором применяется инверсия управления, и тем самым создавать более простой API. А если в нашем «более простом» API недостаточно вариантов использования, тогда наши пользователи могут применить те же самые строительные блоки, которые мы использовали для создания нашего высокоуровневого API, чтобы разрабатывать решения для более сложных задач. Им не нужно просить нас добавить новую функцию в filterWithOptions и ждать, пока она будет реализована. У них уже есть инструменты при помощи которых они могут самостоятельно разрабатывать нужный им дополнительный функционал.
Можно создавать специальный функционал для любой ситуации которая часто встречается у вас.
Примеры из реальной жизни
Существуют различные паттерны, с которыми вы, возможно, уже знакомы, и которые являются просто одной из форм инверсии управления.
Вот два моих любимых паттерна, которые демонструют это «Compound Components» и «State Reducers». Ниже идут краткие примеры того, как можно применять эти паттерны.
Составные Компоненты (Compound Components)
Когда вы думаете о том, как создать хороший API для людей, которые пытаются сделать что-то немного по-другому, вместо того чтобы тянуться к if оператору, попробуйте инвертировать управление. Что если мы передадим ответственность за визуализацию меню на пользователя? Используем одну из самых сильных сторон Реакта:
Важный момент, на который следует обратить внимание — здесь нет состояния ( state ) видимого пользователю компонентов. Состояние неявно разделяется между этими компонентами. Это основная ценность паттерна составных компонентов. Используя эту возможность, мы предоставили некоторый контроль над рендерингом пользователю наших компонентов, и теперь добавление дополнительной строки (или чего-то еще) является простым и интуитивно понятным действием. Никакой дополнительной документации, никаких дополнительных функций, никакого лишнего кода или тестов. Все в выигрыше.
Вы можете почитать больше об этом паттерне здесь. Спасибо Райану Флоренсу, который научил меня этому.
Редюсер Состояния (State Reducer)
Вместо этого я сделал API таким, чтобы пользователи сами могли контролировать как происходят изменения состояния. Думайте о состоянии редюсера ( state reducer ) как о состояний функции, которая вызывается каждый раз, когда изменяется состояние компонента, и дает разработчику приложения возможность влиять на изменение состояния, которое должно произойти.
Пример использования библиотеки Downshift таким образом чтобы она не закрывала меню после того как пользователь нажал на выбранный элемент:
После того как мы добавили этот проп, мы стали получать НАМНОГО меньше запросов на добавление новых настроек для этого компонента. Компонент стал более гибким, и разработчикам стало проще настраивать его так как нужно именно им.
Рендер-пропсы (Render Props)
Стоит упомянуть паттерн «render props». Этот паттерн является идеальным примером использования инверсии управления, но он нам особо больше и не нужен. Подробнее об этом здесь: why we don’t need Render Props as much anymore.
Предупреждение
Инверсия управления это потрясающий способ обойти проблему неправильного предположения о том как ваш код будет использоваться в будущем. Но прежде чем закончить, мне бы хотелось дать вам несколько советов.
Вернемся к нашему надуманному примеру:
Как и в случае с любыми абстракциями, будьте внимательны, применяйте принцип AHA Programming и избегайте поспешных абстракций!
Выводы
Надеюсь что статья была полезной для вас. Я показал как можно применять концепцию Инверсии Управления в Реакте. Эта концепция, конечно же, применима не только к React (как мы видели на примере функции filter ). В следующий раз когда вы заметите что добавляете очередной оператор if в функцию coreBusinessLogic вашего приложения, подумайте, как вы можете инвертировать управление и перенести логику туда, где она используется (или, если она используется в нескольких местах, вы можете создать более специализированную абстракцию для этого конкретного случая).
Если хотите, можете поиграть с примером из статьи на CodeSandbox.
Удачи и спасибо за внимание!
ПС. Если вам понравилась эта статья, вам возможно понравится это выступление: youtube Kent C Dodds — Simply React
Inversion of Control: Методы реализации с примерами на PHP
О боже, ещё один пост о Inversion of Control
Каждый более-менее опытный программист встречал в своей практике словосочетание Инверсия управления (Inversion of Control). Но зачастую не все до конца понимают, что оно значит, не говоря уже о том, как правильно это реализовать. Надеюсь, пост будет полезен тем, кто начинает знакомится с инверсией управления и несколько запутался.
Другими словами, можно сказать, что все зависимости модулей должны строятся на абстракциях этих модулях, а не их конкретных реализациях.
Рассмотрим пример.
Пусть у нас есть 2 класса — OrderModel и MySQLOrderRepository. OrderModel вызывает MySQLOrderRepository для получения данных из MySQL хранилища. Очевидно, что модуль более высокого уровня (OrderModel) зависит от относительного низкоуровневого MySQLOrderRepository.
Пример плохого кода приведён ниже.
В общем и целом этот код будет отлично работать, выполнять возложенные на него обязанности. Можно было и остановиться на этом. Но вдруг у Вашего заказчика появляется гениальная идея хранить заказы не в MySQL, а в 1С. И тут Вы сталкиваетесь с проблемой — Вам приходится изменять код, который отлично работал, да и ещё и изменения вносить в каждый метод, использующий MySQLOrderRepository.
К тому же, Вы и не писали тесты для OrderModel…
И что же со всем этим делать?
1. Фабричный метод / Абстрактная фабрика
Одним из самых простых способов реализации инверсии управления является фабричный метод (может использоваться и абстрактная фабрика)
Суть его заключается в том, что вместо непосредственного инстанцирования объекта класса через new, мы предоставляем классу-клиенту некоторый интерфейс для создания объектов. Поскольку такой интерфейс при правильном дизайне всегда может быть переопределён, мы получаем определённую гибкость при использовании низкоуровневых модулей в модулях высокого уровня.
Рассмотрим выше приведённый пример с заказами.
Вместо того, чтобы напрямую инстанцировать объект класса MySQLOrderRepository, мы вызовем фабричный метод build для класса OrderRepositoryFactory, который и будет решать, какой именно экземпляр и какого класса должен быть создан.
2. Service Locator
Основная идея паттерна Service Locator заключается в том, чтобы иметь объект, который знает, как получить все сервисы, которые, возможно, потребуются. Главное отличие от фабрик в том, что Service Locator не создаёт объекты, а знает как получить тот или иной объект. Т.е. фактически уже содержит в себе инстанцированные объекты.
Объекты в Service Locator могут быть добавлены напрямую, через конфигурационный файл, да и вообще любым удобным программисту способом.
3. Dependency Injection
Setter injection
При таком методе внедрения в классе, куда внедрятся зависимость, создаётся соответствутющий set-метод, который и устанавливает данную зависимость
Constructor injection
Interface injection
Такой метод внедрения зависимостей очень похож на Setter Injection, затем исключением, что при таком методе внедрения класс, куда внедрятся зависимость, наследуется от интерфейса, который обязует класс реализовать данный set-метод.
Какие проблемы данная реализация не решает?
По правде говоря, я не вижу во внедрении зависимостей каких-то больших недостатков. Это хороший способ сделать класс гибким и максимально независимым от других классов. Возможно это ведёт к излишней абстракции, но это уже проблема конкретной реализации принципа программистом, а не самого принципа
4. IoC-контейнер
IoC-контейнер — это некий контейнер, который непосредственно занимается управлением зависимостями и их внедрениями (фактически реализует Dependency Injection)
IoC-контейнеры присутствует во многих современных PHP-фреймворках — Symfony 2, Yii 2, Laravel, даже в Joomla Framework 🙂
Главное его целью является автоматизация внедрения зарегистрированных зависимостей. Т.е. вам необходимо только лишь указать в конструкторе класса необходимый интерфейс, зарегистрировать конкретную реализацию данного интерфейса и вуаля — зависимость внедрена в Ваш класс
Работа таких контейнеров несколько отличается в различных фреймворках, поэтому предоставляю вам ссылки на официальные ресурсы фреймворков, где описано как работают их контейнеры
Заключение
Тема инверсии управления поднималась уже миллионы раз, сотни постов и тысячи комментариев на эту тему. Но тем не менее, всё также я встречаю людей, вижу код и понимаю, что данная тема ещё не очень популярна в PHP, несмотря на наличие отличных фреймворков, библиотек, позволяющих писать красивый, чистый, читаемый, гибкий код.
Надеюсь статья была кому-то полезная и чей-то код благодаря этому станет лучше.
Пишите свои замечания, пожелания, уточнения, вопросы — буду рад