Практика использования dependency injection

36
ПРАКТИКА ИСПОЛЬЗОВАНИЯ DEPENDENCY INJECTION Виктор Петров, к.ф.-м.н, старший разработчик, Сергей Анпилов, ведущий разработчик, при участии команды разработки Kaspersky Endpoint Security for Windows 1

Upload: platonov-sergey

Post on 18-Jul-2015

553 views

Category:

Software


6 download

TRANSCRIPT

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

ПРАКТИКА ИСПОЛЬЗОВАНИЯ

DEPENDENCY INJECTIONВиктор Петров, к.ф.-м.н, старший разработчик,

Сергей Анпилов, ведущий разработчик,

при участии команды разработки Kaspersky Endpoint Security for Windows

1

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

Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:

Использование Service Locator. Подводные камни;

Использование Dependency Injection. Подводные камни;

Обзор современных C++ DI-фреймворков. Перспективы;

Содержание

2

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

Java/C# разработчик и Dependency Injection

C++ разработчик и Dependency Injection

C++ разработчик и Dependency Injection на C++11/14

3

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

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

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

5

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

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

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

7

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

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

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

9

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

Из опыта разработки новой версии Kaspersky Endpoint

Security for Windows:

Использование Service Locator. Подводные камни;

Использование Dependency Injection. Подводные камни;

Обзор современных C++ DI-фреймворков. Перспективы;

Содержание

10

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

Components

AVP Seamless Update Service

AVP AVPSUS

Updater

Firewall

Antimalware

Antivirus

DriverScheduler

Message

Queue

11

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

Использование 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

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

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

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

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:\\");

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

AVP: unit-тестирование

Service Locator Component Dependency Injection Component

VirusDetectorC

IVirusBasesI

VirusBasesMockC

IServiceLocatorI

ServiceLocatorMockC

VirusDetectorC IVirusBasesI

VirusBasesMockC

См. также «получение списка зависимостей»15

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

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

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

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

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

Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:

Использование Service Locator. Подводные камни;

Использование Dependency Injection. Подводные камни;

Обзор современных C++ DI-фреймворков. Перспективы;

Содержание

18

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

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();}

}};

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

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

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

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

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

Из опыта разработки новой версии Kaspersky Endpoint Security for Windows:

Использование Service Locator. Подводные камни;

Использование Dependency Injection. Подводные камни;

Обзор современных C++ DI-фреймворков.Перспективы;

Содержание

22

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

DI фреймворк: перспектива развития AVPSUS

DI фреймворк – средство конструирования объектов на основе

заданной конфигурации зависимостей.

Мотивация:

• Отделение конфигурации от логики;

• Декларативность описания конфигурации;

23

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

Технические требования:

• Простота конфигурирования компонентов;

• Явность списка зависимостей компонент;

• Обнаружение ошибок конфигурации на этапе компиляции;

• Отсутствие необходимости изменять код компонентов;

• Минимальные зависимости на этапах сборки и выполнения;

Общие требования:

• Лицензия допускает коммерческое использование;

• Прохождение апробации в крупных проектах;

• Активное развитие и поддержка проекта;

DI фреймворк: требования AVPSUS

24

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

[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

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

[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

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

[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

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

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

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

[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

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

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

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

[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

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

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

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

PocoCapsule

WallarooGoogle

FruitBoost.DI

manualDI

Простота конфигурирования компонентов

Да Да Да Да Нет

Явность списка зависимостей компонент

Нет Нет Да Нет Да

Обнаружение ошибок конфигурации на этапе компиляции

Нет Нет Да Да Да

Отсутствие необходимости изменять код компонентов

Да Нет Да Да Да

Минимальные зависимости на этапах сборки и выполнения

Нет Да Да Да Да

33

DI фреймворки: сравнение

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

PocoCapsule

WallarooGoogle

FruitBoost.DI

manualDI

Лицензия допускает коммерческое использование

LGPL Да Да Да Да

Прохождение апробации в крупных проектах

Нет Нет Нет Нет Да

Активное развитие и поддержка проекта

Нет Да Да Да Да

34

DI фреймворки: сравнение

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

DI в современном C++: выводы

• Dependency Injection предпочтительнее Service Locator для связывания объектов;

• Для C++-проекта с инверсией зависимости на текущий момент предпочтительнее использовать manual DI либо DI-фреймворк собственной разработки для связывания объектов.

35

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

Ссылки

• 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