Практика использования Dependency Injection
-
Upload
platonov-sergey -
Category
Software
-
view
552 -
download
6
Transcript of Практика использования Dependency Injection
ПРАКТИКА ИСПОЛЬЗОВАНИЯ
DEPENDENCY INJECTIONВиктор Петров, к.ф.-м.н, старший разработчик,
Сергей Анпилов, ведущий разработчик,
при участии команды разработки Kaspersky Endpoint Security for Windows
1
Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:
Использование Service Locator. Подводные камни;
Использование Dependency Injection. Подводные камни;
Обзор современных C++ DI-фреймворков. Перспективы;
Содержание
2
Java/C# разработчик и Dependency Injection
C++ разработчик и Dependency Injection
C++ разработчик и Dependency Injection на C++11/14
3
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
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
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
Из опыта разработки новой версии Kaspersky Endpoint
Security for Windows:
Использование Service Locator. Подводные камни;
Использование Dependency Injection. Подводные камни;
Обзор современных C++ DI-фреймворков. Перспективы;
Содержание
10
Components
AVP Seamless Update Service
AVP AVPSUS
Updater
Firewall
Antimalware
Antivirus
DriverScheduler
Message
Queue
11
Использование Service Locator в AVPstruct 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
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
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:\\");
AVP: unit-тестирование
Service Locator Component Dependency Injection Component
VirusDetectorC
IVirusBasesI
VirusBasesMockC
IServiceLocatorI
ServiceLocatorMockC
VirusDetectorC IVirusBasesI
VirusBasesMockC
См. также «получение списка зависимостей»15
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
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
Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:
Использование Service Locator. Подводные камни;
Использование Dependency Injection. Подводные камни;
Обзор современных C++ DI-фреймворков. Перспективы;
Содержание
18
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();}
}};
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
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
Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:
Использование Service Locator. Подводные камни;
Использование Dependency Injection. Подводные камни;
Обзор современных C++ DI-фреймворков.Перспективы;
Содержание
22
DI фреймворк: перспектива развития AVPSUS
DI фреймворк – средство конструирования объектов на основе
заданной конфигурации зависимостей.
Мотивация:
• Отделение конфигурации от логики;
• Декларативность описания конфигурации;
23
Технические требования:
• Простота конфигурирования компонентов;
• Явность списка зависимостей компонент;
• Обнаружение ошибок конфигурации на этапе компиляции;
• Отсутствие необходимости изменять код компонентов;
• Минимальные зависимости на этапах сборки и выполнения;
Общие требования:
• Лицензия допускает коммерческое использование;
• Прохождение апробации в крупных проектах;
• Активное развитие и поддержка проекта;
DI фреймворк: требования AVPSUS
24
[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“/><beanclass=”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
[C++98] PocoCapsule
virus_detector_reflx.dll
virus_
detector.h
virus_
detector.xml
pxgenproxy
virus_
detector.cc
main.exe
main.cpppococapsule.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
[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
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
[C++11] Google Fruitclass 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 : publicIVirusDetector {
shared_ptr<IVirusBases> m_bases;
public:
VirusDetector(constshared_ptr<IVirusBases>& bases)
: m_bases(bases) {}
virtual void Detect() override {
m_bases->Check();
}
};
29
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
[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
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
PocoCapsule
WallarooGoogle
FruitBoost.DI
manualDI
Простота конфигурирования компонентов
Да Да Да Да Нет
Явность списка зависимостей компонент
Нет Нет Да Нет Да
Обнаружение ошибок конфигурации на этапе компиляции
Нет Нет Да Да Да
Отсутствие необходимости изменять код компонентов
Да Нет Да Да Да
Минимальные зависимости на этапах сборки и выполнения
Нет Да Да Да Да
33
DI фреймворки: сравнение
PocoCapsule
WallarooGoogle
FruitBoost.DI
manualDI
Лицензия допускает коммерческое использование
LGPL Да Да Да Да
Прохождение апробации в крупных проектах
Нет Нет Нет Нет Да
Активное развитие и поддержка проекта
Нет Да Да Да Да
34
DI фреймворки: сравнение
DI в современном C++: выводы
• Dependency Injection предпочтительнее Service Locator для связывания объектов;
• Для C++-проекта с инверсией зависимости на текущий момент предпочтительнее использовать manual DI либо DI-фреймворк собственной разработки для связывания объектов.
35
Ссылки
• 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