Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Dependency Injection, маловато инфы в рунете 
:(
    Опции темы
SABROG
  Дата 12.8.2010, 21:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



Пытаюсь сделать "правильный" дизайн приложения. Есть классы типа Settings, DownloadManager, ScriptManager, JobManager.
И 3 класса должны иметь доступ к Settings, чтобы читать и писать свои данные. Напрашивается синглтон, но мы знаем, что это антипаттерн и поэтому он быстро "распрашивается" обратно. Альтернатива - передача указателя на Settings в конструктор каждого объекта (или через метод setSettings, что менее рекомендуемо). По сути советуют использовать Dependency Injection / Inversion Of Control (IoC) (пока разницы не понял, говорят, что одно частный случай другого). Сам паттерн не ограничивается передачей указателя на экземпляр класса, это должен быть указатель на интерфейс для конкретного класса типа ISettings, чтобы жестко не привязывать объекты друг к другу (забыл как называется это принцип ООП). Собственно на этой стадии реализация паттерна не идеальна (уже не помню почему), поэтому предлагают использовать так называемый контейнер (DI Container). Это разновидность шаблона Service Locator'а, где регистрируется связка КлассИнтерфейса=КонкретныйКласс. Обычно эти контейнеры настолько наворочены, что их выделяют в отдельные фреймворки и библиотеки. Собственно это то к чему я пришел убегая от сиглтона.

Почему в рунете так мало информации о паттерне Dependency Injection? А в англоязычных ресурсах информация представлена на 90% с примерами на Java. Я понимаю, что шаблон придумали именно разработчики Java, но неужели проблемы не существует на C++, что почти не реально найти примеры и классы? Что используете в разработке вместо Одиночек?


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
boostcoder
Дата 12.8.2010, 22:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



я бы предпочел синглтон. хоть это и антипаттерн, но иногда без него никуда.
второй вариант - передавать ссылку на Settings. чтоб не привязываться к типу, тип ссылки можно передать как шаблонный тип. ну или интерфейс(как вы и писали).

Это сообщение отредактировал(а) boostcoder - 12.8.2010, 22:55
PM WWW   Вверх
JackYF
Дата 12.8.2010, 23:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


полуавантюрист
****


Профиль
Группа: Участник
Сообщений: 5814
Регистрация: 28.8.2004
Где: страна тысячи озё р

Репутация: 18
Всего: 162



Передаю, грубо говоря, shared_ptr< [const] Settings > в конструктор. Собственно, не очень понял, что такое Dependency Injection и чем оно лучше даже того же синглтона.


--------------------
Пожаловаться на меня как модератора можно здесь.
PM MAIL Jabber   Вверх
djamshud
Дата 12.8.2010, 23:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Пердупержденный
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 23.11.2009

Репутация: 8
Всего: 39



Предложу анти в кубе не-паттерн: глобальный объект сеттингс. У вас (не теоретически, а практически!) может быть одновременно несколько наборов настроек, параллельно живущих и использующихся во время работы программы? Нет? Тогда плюньте на теоретика Александреску и решайте свою задачу, а не высосанную из пальца проблему.

Добавлено через 37 секунд
Впрочем и синглтон в равной степени подходит...


--------------------
'Cuz I never walk away from what I know is right
Alice Cooper - Freedom
PM   Вверх
SABROG
Дата 13.8.2010, 00:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



Цитата(boostcoder @  12.8.2010,  22:55 Найти цитируемый пост)
я бы предпочел синглтон

Цитата(JackYF @  12.8.2010,  23:14 Найти цитируемый пост)
чем оно лучше даже того же синглтона.

Цитата(djamshud @  12.8.2010,  23:32 Найти цитируемый пост)
Впрочем и синглтон в равной степени подходит... 


Лучше тем, что не singleton с его недостатками. В принципе не сложно писать такие цепочки:

Код

int main(int argc, char* argv[])
{
    ServiceLocator loc;
    A a(&loc);
    return 0;
}
...
void A::A(ServiceLocator* loc)
{
    m_locator = loc;
}
...
void A::foo()
{
    C c = new C(m_locator);
}


Другое дело, что вариант не идеален,  а как работают DI Container'ы для C++ я пока не до конца разобрался.


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
Любитель
Дата 13.8.2010, 01:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Программист-романтик
****


Профиль
Группа: Комодератор
Сообщений: 3645
Регистрация: 21.5.2005
Где: Воронеж

Репутация: 24
Всего: 92



Во-первых, с иок-ом, как таковым, всё просто - не делай зависимости класса A от класса Settings. Делай интерфейс ISettings и работай с ним. 

Но, во-вторых, это неудобно - т. к. придётся передавать указатель на объект настроек. "Инъекция" возможна только в том случае, если все объекты мы получаем через DI-контейнер. Т. е. в итоге мы:
1. Регистрируем (где-нить при старте приложения) маппинг: ISettings -> DbSettings, или XmlSettings, или IniSettings (да, вообще логично что Settings - это чисто объект данных, а методы load/save/etc. в SettingsManager-е каком-нибудь, ну да ладно).
2. Добавляем параметр типа ISettings в конструктор нашего класса A (инъекция в конструктор - самая простейшая и достаточно популярная).
3. Вместо создания объекта класса A пишем container.Resolve<A>().

В общем-то всё красиво. У DbSettings у нас будет IDbConnection (я условно), который тоже автоматом зарезольвится на что надо - и т. д. Более того, любой DI-контейнер на практике действительно является и сервис локатором ("чистый" DI-контейнер должен производить только "инъекции", а создание/получение объектов - не его дело, но на практике такого не бывает). А это значит, что мы также можем регулировать время жизни объектов и пр.

Теперь проблемы. Любой настоящий DI-контейнер реализуется с использованием рефлекшена. Именно поэтому это (в основном) ява/шарп. В чистом С++ "правильного" DI вообще не может быть. С учёток оберток, предоставляющих метаданные (как в том ж Qt) - в принципе сделать вомзожно, но я не встречал готовых реализаций.

В случае С++ на мой взгляд есть 2 варианта:
1. Если нужна действительна абстракция - простейшая фабрика, без каких либо наворотов.
2. Если нет - то синглтон smile

Более того - даже в случае явы/шарпа я бы не стал городить иерархии классов/интерфейсов и полную абстракцию, если это бы действительно не было бы нужно.


--------------------
PM MAIL ICQ Skype   Вверх
SABROG
Дата 13.8.2010, 07:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



Цитата(Любитель @  13.8.2010,  01:43 Найти цитируемый пост)
С учёток оберток, предоставляющих метаданные (как в том ж Qt) - в принципе сделать вомзожно, но я не встречал готовых реализаций.

Они есть:

QNimbleContainer
QtIocContainer

Другое дело, что они жестко привязаны к мета-объектной системе и как следствие любой класс должен наследовать QObject. К тому же не хотелось бы использовать сторонние библиотеки/фреймворки ради того, чтобы просто уйти от синглтона.


Цитата(Любитель @  13.8.2010,  01:43 Найти цитируемый пост)
Если нужна действительна абстракция - простейшая фабрика, без каких либо наворотов.

То есть реализовать Service Locator через фабрику и передавать указатель на IServiceLocator через конструктор в каждый объект, типа этого?
Цитата(Любитель @  13.8.2010,  01:43 Найти цитируемый пост)
Если нет - то синглтон  smile 

Эхх, если начать реализацию синглтона, то помимо идеологических проблем существует и физиологическая - проблема с потоко-безопасностью. Чтобы его сделать потоко-безопасным придется прибегнуть к double checked locking паттерну реализованному через атомарные операции, которых пока нет в C++, это значит надо использовать сторонние библиотеки, BOOST например (ну или начать использовать функции из C++0x)


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
Любитель
Дата 13.8.2010, 09:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Программист-романтик
****


Профиль
Группа: Комодератор
Сообщений: 3645
Регистрация: 21.5.2005
Где: Воронеж

Репутация: 24
Всего: 92



Цитата(SABROG @  13.8.2010,  07:19 Найти цитируемый пост)
QtIocContainer

Совсем не впечатлило. Всё делать через плагины - это жёстко.

Цитата(SABROG @  13.8.2010,  07:19 Найти цитируемый пост)
QNimbleContainer

Здесь уже гораздо лучше. Беглый просмотр блога говорит о том, что направление выбрано верное. Но.. насколько это production ready - уже другой вопрос (всё-таки пока похоже больше на грамотное увлечение).

Цитата(SABROG @  13.8.2010,  07:19 Найти цитируемый пост)
Другое дело, что они жестко привязаны к мета-объектной системе и как следствие любой класс должен наследовать QObject.

Ну.. так или иначе они будут расчитывать на некоторый базовый класс - для эмуляции рефлекшена (черех собственное хранилище метаданных). Ну или точнее так - в принципе базовый класс не обязательно, но на хранилище метаданных обязательно (ну плюс там ещё проблема универсальной передачи параметров появляется, решаемая в Qt через QVariant).

Цитата(SABROG @  13.8.2010,  07:19 Найти цитируемый пост)
То есть реализовать Service Locator через фабрику и передавать указатель на IServiceLocator через конструктор в каждый объект, типа этого?

Ну.. Service Locator реально нужен когда? Когда у нас накапливается много объектов, создаваемых через фабрики. И особенно, если при этом между ними есть какие-то связи. Т. е. по сути это централизованная фабрика. Т. е. в простых случаях достаточно обычных фабрик (per class). Далее - передавать указатель на IServiceLocator я бы не стал. Уж саму фабрику точно делал бы синглтоном, ну или просто статик поля/методы. Смысл абстрагировать абстракцию?

Цитата(SABROG @  13.8.2010,  07:19 Найти цитируемый пост)
Эхх, если начать реализацию синглтона, то помимо идеологических проблем существует и физиологическая - проблема с потоко-безопасностью.

Во-первых, в плане life scope в случае навороченных DI-контейнеров всегда есть опция синглетона. Наличие одного (и только одного) экземпляра класса в системе - не есть плохо. Плохо то, что мы жёстко зависимы от этого.

Во-вторых - будут ли проблемы с потоками, это зависит от конкретного кода объекта. И в большинстве случаев наиболее удачным решением будет просто использование TLS а не локи (по крайней мере уровень паралелизма будет гораздо выше). Любой уважающий себя компилятор это поддерживает, одним дефайном решается необходимость привязки к конкретному компилеру.


--------------------
PM MAIL ICQ Skype   Вверх
Earnest
Дата 13.8.2010, 10:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 53
Всего: 183



Цитата(Любитель @  13.8.2010,  10:39 Найти цитируемый пост)
 И в большинстве случаев наиболее удачным решением будет просто использование TLS а не локи 

TLS - это хороший выход только если каждый поток должен иметь свой экземпляр. А это вроде бы не так. Если система не очень сложная, может, не стоит парится с thread-safe синглетона. Т.е. доступ к данным действительно нужно синхронизировать.
А "double checked locking паттерн" реально нужен только, если возможны накладки при создании или уничтожении.


--------------------
...
PM   Вверх
Леопольд
Дата 13.8.2010, 10:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(SABROG @  13.8.2010,  07:19 Найти цитируемый пост)
Чтобы его сделать потоко-безопасным придется прибегнуть к double checked locking паттерну реализованному через атомарные операции, которых пока нет в C++
Если я правильно понял настройки должны быть в одном инстансе, доступ к которому осуществляется из нескольких потоков. Как то можно гарантироваить безопастность доступа без какой-либо синхронизации? Ведь в любом случае придётся использовать какую-то блокировку, нет?


Это сообщение отредактировал(а) Леопольд - 13.8.2010, 10:20


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
Леопольд
Дата 13.8.2010, 10:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(Earnest @  13.8.2010,  10:11 Найти цитируемый пост)
А "double checked locking паттерн" реально нужен только, если возможны накладки при создании или уничтожении. 
Цитата(SABROG @  13.8.2010,  07:19 Найти цитируемый пост)
придется прибегнуть к double checked locking
Может просто создать объект в статической памяти?



--------------------
вопросов больше чем ответов
PM MAIL   Вверх
Леопольд
Дата 13.8.2010, 11:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

Репутация: 10
Всего: 13



Цитата(SABROG @  13.8.2010,  07:19 Найти цитируемый пост)
придется прибегнуть к double checked locking паттерну реализованному через атомарные операции, которых пока нет в C++
Код
#include <boost/thread/mutex.hpp>

template<typename T>
struct Singleton{
    static T& instance(){
        if(!instance_){
            boost::mutex::scoped_lock lock(mutex_);
            if(!instance_)
                instance_ = new T();
        }
        return const_cast<T&>(*instance_);
    }
private:
    static volatile T* instance_;
    static boost::mutex mutex_;
};
template<typename T>
volatile T* Singleton<T>::instance_ = 0;
template<typename T>
boost::mutex Singleton<T>::mutex_;

Не пойму, зачем здесь атомарные операции? Любой поток будет залочен до тех пор, пока объект не будет полностью сконструирован.


Это сообщение отредактировал(а) Леопольд - 13.8.2010, 11:34


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
SABROG
Дата 13.8.2010, 11:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



Цитата(Любитель @  13.8.2010,  09:39 Найти цитируемый пост)
ну или просто статик поля/методы

Цитата(Леопольд @  13.8.2010,  10:39 Найти цитируемый пост)
Может просто создать объект в статической памяти?

QtCreator для кода:

Код

Core::EditorManager::instance();


Использует разновидность синглтона Мейерса. Только если в оригинале он не потоко-безопасен, то у них подход хитрее.
Предполагается, что любое приложение изначально создается как single threaded, поэтому пока мы не наплодили других потоков предварительно происходит инициализация всех "синглтонов", например где-нибудь в main(). Сам вариант приблизительно выглядит так:

Код

// .h
class EditorManager
{
public:
    EditorManager()
    {
         m_instance = this;
    }
    static EditorManager* instance() {return m_instance;}
private:
    static EditorManager* m_instance;
};
...
// .cpp
static EditorManager::m_instance = 0;
...
// main.cpp
int main(int argc, char* argv[])
{
    (void)new EditorManager; // создание экземпляра одиночки
}



Недостатки такого подхода очевидны, но я пока склоняюсь к этому варианту, как наиболее простому и хоть частично потоко-безопасном по сравнению с таким вариантом, который без синхронизации потоков вообще не безопасен:
Код

    static GlobalClass *instance()
    {
        if (!s_instance)
          s_instance = new GlobalClass;
        return s_instance;
    }



Цитата(Любитель @  13.8.2010,  09:39 Найти цитируемый пост)
Смысл абстрагировать абстракцию?

Чтобы отвязаться от класса ServiceLocator, впихнуть тесты, при желании подсунуть "заглушку".

Цитата(Earnest @  13.8.2010,  10:11 Найти цитируемый пост)
Если система не очень сложная, может, не стоит парится с thread-safe синглетона. Т.е. доступ к данным действительно нужно синхронизировать.
А "double checked locking паттерн" реально нужен только, если возможны накладки при создании или уничтожении. 

Предположим я хочу научиться делать автомобили. Специалисты отправляют меня сначала научиться делать велосипеды. Когда я научусь их делать, то настанет очередь чего-то более сложного. То есть на данном этапе мне важно не быстрей что-либо написать, а понять как делать это правильно в будущем, когда такая необходимость настанет.

Цитата(Леопольд @  13.8.2010,  10:20 Найти цитируемый пост)
Как то можно гарантироваить безопастность доступа без какой-либо синхронизации? Ведь в любом случае придётся использовать какую-то блокировку, нет?

В случае заранее проинициализированного указателя проблем атомарного чтения самого указателя возникнуть не должно (по крайней мере на AI-32), а работу с данными на которые он указывает нужно синхронизировать, благо в Qt достаточно поток-безопасных классов и Qt сама берет на себя эту рутину. Тут конечно остается проблема с "висячим" объектом синглтона, который может быть даже и не используется нигде в коде (но это редкий случай, нафига его тогда вообще создавать).

Цитата(Леопольд @  13.8.2010,  11:22 Найти цитируемый пост)
Не пойму, зачем здесь атомарные операции? Любой поток будет залочен до тех пор, пока объект не будет полностью сконструирован.


Во первых мутекс блокирует работу всех остальных потоков, которые пытаются получить доступ к синглтону, а могли бы делать полезную работу. Если дальше копать, то там нужен второй мутекс, а потом в итоге и он не спасает, погугли на "Double Checked Locking Broken".

Как я уже упомянул нужен "безопасный" вариант шаблона Double Checked Locking, который реализован в функции boost::callonce(), вот пример нормального потоко-безопасного синглтона: http://www.boostcookbook.com/Recipe:/1235044

Но это мне не подходит, так как я пока не хочу тянуть boost, да еще и ради антипаттерна.

Это сообщение отредактировал(а) SABROG - 13.8.2010, 11:49


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
Любитель
Дата 13.8.2010, 11:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Программист-романтик
****


Профиль
Группа: Комодератор
Сообщений: 3645
Регистрация: 21.5.2005
Где: Воронеж

Репутация: 24
Всего: 92



Цитата(SABROG @  13.8.2010,  11:37 Найти цитируемый пост)
Недостатки такого подхода очевидны, но я пока склоняюсь к этому варианту, как наиболее простому и хоть частично потоко-безопасном по сравнению с таким вариантом, который без синхронизации потоков вообще не безопасен:

А, так ты ведёшь речь именно про создание инстанса. А я то думал про "небезпоасные" его методы. Если ты уверен в безопасности методов - то очевидно, что тут и обычные объекты синхронизации (т. е. обычная блокировка) подойдут:
Код

if (_instance)
  return _instance;

_mutex.lock();
if (!_instance)
  _instance = new MyClass;
_mutex.release();

return _instance;


Цитата(SABROG @  13.8.2010,  11:37 Найти цитируемый пост)
Чтобы отвязаться от класса ServiceLocator, впихнуть тесты, при желании подсунуть "заглушку".

Ну.. А создание сервис локатора кто-то ж будет делать? Потом будем это создание абстрагировать и т. д. Я не очень вижу в этом смысл. Заглушку на классы, занимающиеся "функционалом" - ок. Тесты - ок. Но зачем сервис-локатор абстрагировать? Единственный случай, который можно придумать - это желание использовать для него какой-т фреймворк и возможность переключение между разными фреймворками (не в процессе, а в перспективе проекта). Но.. это только если речь про достаточно нетривиальную реализацию.


--------------------
PM MAIL ICQ Skype   Вверх
SABROG
Дата 13.8.2010, 12:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 4
Всего: 91



Цитата(Любитель @  13.8.2010,  11:48 Найти цитируемый пост)
(т. е. обычная блокировка) подойдут

Так это и есть тот самый "сломанный" вариант Double Checked Locking паттерна.

Цитата(Любитель @  13.8.2010,  11:48 Найти цитируемый пост)
Но зачем сервис-локатор абстрагировать? Единственный случай, который можно придумать - это желание использовать для него какой-т фреймворк и возможность переключение между разными фреймворками (не в процессе, а в перспективе проекта). Но.. это только если речь про достаточно нетривиальную реализацию.

Зачем-то разработчики Microsoft его используют (IServiceLocator + IServiceProvider). Видимо, чтобы конечный пользователь мог подставлять собственные фабрики. К тому же опять идет жесткая привязка к конкретной реализации:

user posted image


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1570 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.