Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Практика использования Dependency Injection

2,504 views

Published on

В докладе рассказывается об опыте применения «инверсия управления» (Inversion of Control) при разработке новой версии KES. Этот подход заключается в том, что более высокоуровневый код не зависит напрямую от конкретной реализации нижележащего слоя. Вместо этого он зависит от абстрактного протокола (интерфейса), конкретный же компонент подставляется конфигурационным кодом-клиентом. Эта практика позволяет понизить loose coupling программных модулей и применяется практически в любых крупных проектах.

При разработке новой версии KES было принято решение изменить подход к реализации инверсии управления. Было решено отказаться от централизованного обобщенного реестра доступных компонентов (шаблон (паттерн) Service Locator) в пользу явной передачи зависимостей конфигуратором (ручная инъекция зависимостей (manual Dependency Injection)). При это возникли проблемы с использованием готовых библиотек Dependency Injection Frameworks. Применение подобных библиотек стало стандартом в мире разработки Java/C# за последние 10-15 лет, но в мире C++ они пока не получили подобного распространения. В докладе делается обзор и сравнение актуальных DI-Framework’ов на C++, анализируется их применимость к практическим задачам ЛК. Анализируется, что могут привнести стандарты C++11/14 для упрощения решения таких задач.

Published in: Software
  • Be the first to comment

  • Be the first to like this

Практика использования Dependency Injection

  1. 1. ПРАКТИКА ИСПОЛЬЗОВАНИЯ DEPENDENCY INJECTION Виктор Петров, к.ф.-м.н, старший разработчик, Сергей Анпилов, ведущий разработчик, при участии команды разработки Kaspersky Endpoint Security for Windows 1
  2. 2. Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:  Использование Service Locator. Подводные камни;  Использование Dependency Injection. Подводные камни;  Обзор современных C++ DI-фреймворков. Перспективы; Содержание 2
  3. 3. Java/C# разработчик и Dependency Injection C++ разработчик и Dependency Injection C++ разработчик и Dependency Injection на C++11/14 3
  4. 4. Service Locator (SL) и Dependency Injection (DI) – способы реализации инверсии управления (Inversion of Control, IoC) class VirusDetector { shared_ptr<VirusBases> m_virusBases; public: VirusDetector() { m_virusBases = make_shared<VirusBases>(); } ... }; Прямое управление VirusDetectorC VirusBasesC 4
  5. 5. 5
  6. 6. class VirusDetector { shared_ptr<IVirusBases> m_virusBases; public: explicit VirusDetector( shared_ptr<IVirusBases> virusBases) : m_virusBases(move(virusBases)) { } ... }; VirusDetectorC IVirusBasesI VirusBasesC 6 Service Locator (SL) и Dependency Injection (DI) – способы реализации инверсии управления (Inversion of Control, IoC) Dependency Injection
  7. 7. 7
  8. 8. VirusDetectorC IVirusBasesI VirusBasesC class VirusDetector { shared_ptr<IVirusBases> m_virusBases; public: explicit VirusDetector(IServiceLocator* sl): m_virusBases(sl->GetInterface(IID_OF(IVirusBases))) { } ... }; IServiceLocatorI ServiceLocatorC 8 Service Locator (SL) и Dependency Injection (DI) – способы реализации инверсии управления (Inversion of Control, IoC) Service Locator
  9. 9. 9
  10. 10. Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:  Использование Service Locator. Подводные камни;  Использование Dependency Injection. Подводные камни;  Обзор современных C++ DI-фреймворков. Перспективы; Содержание 10
  11. 11. Components AVP Seamless Update Service AVP AVPSUS Updater Firewall Antimalware Antivirus Driver Scheduler Message Queue 11
  12. 12. Использование Service Locator в AVP struct IServiceLocator : public IObject { virtual shared_ptr<IObject> GetInterface(iid_t iid) = 0; }; struct IObjectFactory : public IObject { virtual shared_ptr<IObject> CreateInstance() = 0; }; <component name="VirusBases" clsid="0xfe1fe107" module="virus_bases.dll"> <interface name="IVirusBases" iid="0x7996082a" /> </component> shared_ptr<IObjectFactory> getObjectFactory(IServiceLocator*, clsid_t); // export config dll source 12
  13. 13. AVP: получение списка зависимостей Virus Detector GetInterface(IID_OF (IVirusBasesN), basesN) Service Locator Component Dependency Injection Component class VirusDetector { public: VirusDetector( shared_ptr<IVirusBases1> bases1, ..., shared_ptr<IVirusBasesM> basesM); ... }; 13
  14. 14. IDiskFormater* df AVP: контроль используемых сервисов Service Locator Component Dependency Injection Component 14 void UpdateBases(IServiceLocator* sl) { ... OptimizeBasesFormat(sl); } void OptimizeBasesFormat(IServiceLocator* sl) { } // TODO: Defragment diskshared_ptr<IDiskFormater> diskFormater = sl->GetInterface(IID_OF(IDiskFormater)); diskFormater->Format("C:"); df void UpdateBases( ... OptimizeBasesFormat( } ) { void OptimizeBasesFormat( ); ) {IDiskFormater* df } // TODO: Defragment diskdf->Format("C:");
  15. 15. AVP: unit-тестирование Service Locator Component Dependency Injection Component VirusDetectorC IVirusBasesI VirusBasesMockC IServiceLocatorI ServiceLocatorMockC VirusDetectorC IVirusBasesI VirusBasesMockC См. также «получение списка зависимостей»15
  16. 16. DI в разных типах приложений Service Locator Component Dependency Injection Component SL Application DI Application Application with SL of another type Non-IoC Application SL Application DI Application Application with SL of another type Non-IoC Application Adapter SL component DI component SL component SL component SL component Adapter Adapter DI component DI component DI component 16
  17. 17. AVPSUS: комбинирование DI и SL template<typename T, typename... Deps> shared_ptr<T> Adapter::CreateSLComponent(Deps... dependencies) { shared_ptr<IServiceLocator> sl = BuildServiceLocator(dependencies...); shared_ptr<T> component = CreateObject<T>(sl); return component; } DI application Adapter SL component 17
  18. 18. Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:  Использование Service Locator. Подводные камни;  Использование Dependency Injection. Подводные камни;  Обзор современных C++ DI-фреймворков. Перспективы; Содержание 18
  19. 19. AVPSUS: Подводные камни DI 1. Объекты создаются всегда, вне зависимости от частоты использования 19 class VirusDetector { const shared_ptr<IVirusBases> m_virusBases; const shared_ptr<IRecoveryManager> m_recoveryManager; public: VirusDetector(shared_ptr<IVirusBases> virusBases, shared_ptr<IRecoveryManager> recoveryManager) : m_virusBases(move(virusBases)) , m_recoveryManager(move(recoveryManager)) {} void DetectViruses() { try {/* Detect */} catch (const std::exception&) { m_recoveryManager->Recover(); } } };
  20. 20. 2. Разрастание конструкторов - признак плохого дизайна. class VirusDetector { public: VirusDetector(IVirusBases* virusBases, IRecoveryManager* recoveryManager, IAntimalwareDetector* antimalwareDetector, IAntiRootkitDetector* antiRootkitDetector, IWebAnalyzer* webAnalyzer, IMailAnalyzer* mailAnalyzer, ITrafficProcessor* trafficProcessor, IBasesDownloader* basesDownloader, IBasesVerifier* basesVerifier, IWormsDetector* wormsDetector, IVulnerabilityDetector* vulnerabilityDetector, ITrojanDetector* trojanDetector, IStealthDetector* stealthDetector, ILicenseProvider* licenseProvider, ISettingsProvider* settingsProvider, IPhishingDetector* phishingDetector, IUrlFilter* urlFilter, IWoodPeckerDetector* woodPeckerDetector, IMacroVirusesDetector* macroVirusesDetector, IAntiBanner* antiBanner, ISystemWatcher* systemWatcher, IFileMonitor* fileMonitor, IRegistryMonitor* registryMonitor, INetworkMonitor* networkMonitor, IApplcationMonitor* applicationMonitor, IDeviceControl* deviceControl, IInstantMessengersMonitor* instantMessengersMonitor); /*...*/ }; 20 AVPSUS: Подводные камни DI
  21. 21. 3. Рост сложности конфигурации void Configure() { auto vbFactory = CreateVirusBasesFactory(); auto vbConfig = LoadVirusBasesConfiguration(); auto virusBases = vbFactory->CreateVirusBases(vbConfig); auto recoveryManager = TryCreateRecoveryManager(); if (!recoveryManager) LogWarningRecoveryManagerNotAvailable(); auto amDetector = make_shared<AntimalwareDetector>(virusBases); auto arDetector = make_shared<AntiRootkitDetector>(virusBases, amDetector); auto webAnalyzer = make_shared<WebAnalyzer>(amDetector, recoveryManager); auto mailAnalyzer = make_shared<MailAnalyzer>(amDetector, recoveryManager, webAnalyzer); auto trafficProcessor = make_unique<TrafficProcessor>(arDetector, webAnalyzer, mailAnalyzer); // ... } 21 AVPSUS: Подводные камни DI
  22. 22. Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:  Использование Service Locator. Подводные камни;  Использование Dependency Injection. Подводные камни;  Обзор современных C++ DI-фреймворков. Перспективы; Содержание 22
  23. 23. DI фреймворк: перспектива развития AVPSUS DI фреймворк – средство конструирования объектов на основе заданной конфигурации зависимостей. Мотивация: • Отделение конфигурации от логики; • Декларативность описания конфигурации; 23
  24. 24. Технические требования: • Простота конфигурирования компонентов; • Явность списка зависимостей компонент; • Обнаружение ошибок конфигурации на этапе компиляции; • Отсутствие необходимости изменять код компонентов; • Минимальные зависимости на этапах сборки и выполнения; Общие требования: • Лицензия допускает коммерческое использование; • Прохождение апробации в крупных проектах; • Активное развитие и поддержка проекта; DI фреймворк: требования AVPSUS 24
  25. 25. [C++98] PocoCapsule [Detector.xml] <?xml version=”1.0”?> <!DOCTYPE poco-application-context SYSTEM “http://www.pocomatic.com/poco-application- context.dtd”> <poco-application-context> <load library=“./virus_detector.$dll“/> <load library=“./virus_detector_reflx.$dll“/> <bean class=“IVirusBases“ factory- method=“CreateVirusBases“/> <bean class=”VirusDetector” id=”detector” destroy-method=”delete” lazy-init=”false”> <method-arg ref=”IVirusBases”/> <ioc method=”Init”> <method-arg type=”cstring” value=”mode=fast”/> </ioc> </bean> </poco-application-context> [Detector.h] struct IVirusBases {}; class VirusBases : public IVirusBases {}; IVirusBases* CreateVirusBases() { return new VirusBases(); } class VirusDetector { public: explicit VirusDetector(IVirusBases*); void Init(const char*); void Detect(); }; 25
  26. 26. [C++98] PocoCapsule virus_detector_reflx.dll virus_ detector.h virus_ detector.xml pxgenproxy virus_ detector.cc main.exe main.cpp pococapsule.dll [main.cpp] #include <pocoapp.h> int main(int argc, char** argv) { POCO_AppContext* ctxt = POCO_AppContext::create( “virus_detector.xml”, “file”); VirusDetector* det = ctxt->getBean("detector"); det->Detect(); ctxt->terminate(); ctxt->destroy(); } 26
  27. 27. [C++03] Wallaroo [virus_detector.h] #include "wallaroo/registered.h" struct IVirusBases : public wallaroo::Part {}; class VirusBases : public IVirusBases {}; class VirusDetector : public wallaroo::Part { wallaroo::Collaborator<IVirusBases> m_virusBases; public: VirusDetector(IVirusBases* virusBases); void Detect(); }; [virus_detector.cpp] #include "virus_detector.h" WALLAROO_REGISTER(IVirusBases) WALLAROO_REGISTER(VirusBases) WALLAROO_REGISTER(VirusDetector, IVirusBases*) VirusDetector::VirusDetector() : m_virusBases("m_virusBases", RegistrationToken()) {} void VirusDetector::Detect() {} 27
  28. 28. int main(int argc, char* argv[]) { try { Catalog catalog; catalog.Create("virusDetector", "VirusDetector"); catalog.Create("virusBases", "VirusBases"); use(catalog["virusBases"]).as("m_virusBases").of(catalog["virusDetector"]); shared_ptr<VirusDetector> virusDetector = catalog["virusDetector"]; virusDetector->Detect(); } catch (const wallaroo::WallarooError& error) { std::cerr << "ERROR: " << error.what() << std::endl; } return 0; } [C++03] Wallaroo 28
  29. 29. [C++11] Google Fruit class IVirusBases { virtual void Check(string s) = 0; }; class VirusBases : public IVirusBases { public: VirusBases() = default; virtual void Check() override { ... } }; struct IVirusDetector { virtual void Detect() = 0; }; class VirusDetector : public IVirusDetector { shared_ptr<IVirusBases> m_bases; public: VirusDetector(const shared_ptr<IVirusBases>& bases) : m_bases(bases) {} virtual void Detect() override { m_bases->Check(); } }; 29
  30. 30. Component<IVirusDetector> getDetectorComponent() { return fruit::createComponent() .registerConstructor<VirusDetector(IVirusBases*)>() .registerConstructor<VirusBases()>() .bind<IVirusBases, VirusBases>() .bind<IVirusDetector, VirusDetector>(); } int main() { Injector<IVirusDetector> injector(getDetectorComponent()); shared_ptr<IVirusDetector> detector = injector.get<IVirusDetector*>(); detector->Detect(); return 0; } [C++11] Google Fruit 30
  31. 31. [C++03/11/14]Boost.DI struct IVirusBases { virtual void Check() = 0; }; class VirusBases : public IVirusBases {…}; struct IAntimalware { virtual void Scan() = 0; }; class Antimalware : public IAntimalware {...}; class VirusDetector{ shared_ptr<IVirusBases> m_virusBases; shared_ptr<IAntimalware> m_antimalware; bool m_value; public: VirusDetector( const shared_ptr<IVirusBases>& virusBases, const shared_ptr<IAntimalware>& antimalware, bool value) : m_virusBases(virusBases) , m_antimalware(antimalware) , m_value(value) { } int Detect() const { if (m_value) { m_virusBases->Check(); m_antimalware->Scan(); } return 0; } };31
  32. 32. int main() { auto injector = di::make_injector( di::bind<IVirusBases, VirusBases> , di::bind<IAntimalware, Antimalware> , di::bind<bool>.to(true) ); return injector.create<VirusDetector>().Detect(); } [C++03/11/14]Boost.DI 32
  33. 33. Poco Capsule Wallaroo Google Fruit Boost.DI manual DI Простота конфигурирования компонентов Да Да Да Да Нет Явность списка зависимостей компонент Нет Нет Да Нет Да Обнаружение ошибок конфигурации на этапе компиляции Нет Нет Да Да Да Отсутствие необходимости изменять код компонентов Да Нет Да Да Да Минимальные зависимости на этапах сборки и выполнения Нет Да Да Да Да 33 DI фреймворки: сравнение
  34. 34. Poco Capsule Wallaroo Google Fruit Boost.DI manual DI Лицензия допускает коммерческое использование LGPL Да Да Да Да Прохождение апробации в крупных проектах Нет Нет Нет Нет Да Активное развитие и поддержка проекта Нет Да Да Да Да 34 DI фреймворки: сравнение
  35. 35. DI в современном C++: выводы • Dependency Injection предпочтительнее Service Locator для связывания объектов; • Для C++-проекта с инверсией зависимости на текущий момент предпочтительнее использовать manual DI либо DI-фреймворк собственной разработки для связывания объектов. 35
  36. 36. Ссылки • http://www.martinfowler.com/articles/injection.html • http://www.objectmentor.com/resources/articles/dip.pdf • https://code.google.com/p/pococapsule/ • https://code.google.com/p/wallaroo/ • https://github.com/google/fruit • https://github.com/krzysztof-jusiak/di 36

×