четверг, 31 марта 2011 г.

Паттерн Service Locator на C++

Паттерн Service locator представляет собой хранилище сервисных объектов. Фактически это некоторого рода ассоциативный массив с экземплярами объектов-реализаций, которые определяются по ключу – типу или имени интерфейса.

Применяется для внедрения зависимостей (Dependency Injection). В целом очень полезная фича. Иногда этот паттерн реализуется DI контейнерами заодно с основной функциональностью.

Алгоритм работы с Service Locator:
1. Регистрация экземпляров реализаций сервисных объектов в ServiceLocator путем ассоциирования с типом или именем интерфейса
2. Передача экземпляра ServiceLocator в классы, которым необходимы сервисные объекты
3. Класс, которому необходим сервисный объект, запрашивает его по ключу у контенера

Получаем развязку интерфейса и реализации.

Спроектируем и реализуем Service Locator на C++.

Терминология:
Сервис – экземпляр класса, реализующего интерфейс
Интерфейс сервиса – класс, который декларирует методы взаимодействия с сервисом, но не реализовывает их
Тег – строковой идентификатор объекта в контейнере



Актерами выступают:
- конфигуратор – функционал, создающий ServiceLocator и настраивающий сервисы
- пользователь – функционал, использующий сервисы

Конфигуратор может:
- создать ServiceLocator
- регистрировать ассоциацию типа интерфейса с экземпляром объекта сервиса с указанием тега
- регистрировать ассоциацию типа интерфейса с экземпляром объекта сервиса без указания тега

Пользователь может:
- получать сервис по запросу типа интерфейса с указанием тега
- получать сервис по запросу типа интерфейса без указания тега
- получать сервис по запросу имени типа интерфейса с указанием тега
- получать сервис по запросу имени типа интерфейса без указания тега

Реализация

Интерфейс IServiceLocator

#include <string>
#include <list>

class IServiceLocator
{
public:
virtual ~IServiceLocator() {};

virtual void RegisterService(std::string name, void* serviceObject, std::string tag = "") = 0;
virtual void* Locate(std::string name, std::string tag = "") = 0;

template<typename T>
void RegisterService(T* serviceObject, std::string tag = "")
{
RegisterService(typeid(T).name(), reinterpret_cast<void*>(serviceObject), tag);
}

template<typename T>
T* Locate(std::string tag = "")
{
return (T*)Locate(typeid(T).name(),tag);
}
};


Декларация реализации ServiceLocator


#include <list>
#include <string>

class ServiceLocator : public IServiceLocator
{
public:
virtual void RegisterService(std::string name, void* serviceObject, std::string tag = "");
virtual void* Locate(std::string name, std::string tag = "");
virtual ~ServiceLocator();

protected:
struct Service
{
void* ServiceObject;
std::string Name;
std::string Tag;
};

private:
std::list<struct Service> services_;
typedef std::list<struct Service>::iterator ServiceIterator;
};


Реализация ServiceLocator

ServiceLocator::~ServiceLocator()
{
for (ServiceIterator i = services_.begin(); i != services_.end(); i++)
{
if (i->ServiceObject != NULL)
{
delete i->ServiceObject;
i->ServiceObject = NULL;
}
}
services_.clear();
}

void ServiceLocator::RegisterService(std::string name, void* serviceObject, std::string tag)
{
if (serviceObject == NULL)
throw std::invalid_argument("serviceObject");

for (ServiceIterator i = services_.begin(); i != services_.end(); i++)
{
if ((i->Name == name) && (i->Tag == tag))
{
throw std::runtime_error(std::string("Duplicate service registration for '") + name +
std::string("' and tag '") + tag + std::string("'"));
}
}

struct Service service;
service.Name = name;
service.ServiceObject = serviceObject;
service.Tag = tag;

services_.push_back(service);
}

void* ServiceLocator::Locate(std::string name, std::string tag)
{
for (ServiceIterator i = services_.begin(); i != services_.end(); i++)
{
if ((i->Name == name) && (i->Tag == tag))
{
return reinterpret_cast<void*>(i->ServiceObject);
}
}
throw std::runtime_error(std::string("Service not found for '") + name +
std::string("' and tag '") + tag + std::string("'"));
}


Пример использования:

IServiceLocator *serviceLocator = new ServiceLocator();

serviceLocator->RegisterService<IService>(new ServiceRelease());
serviceLocator->RegisterService<IService>(new OtherServiceRelease(),"svc");

IService* service = serviceLocator->Locate<IService>();
IService* otherService = serviceLocator->Locate<IService>("svc");

Комментариев нет:

Отправить комментарий