Russian Qt Forum

Программирование => С/C++ => Тема начата: once_again_abc от Август 26, 2014, 09:51



Название: Dependency injection
Отправлено: once_again_abc от Август 26, 2014, 09:51
Посоветуйте хорошее описание паттерна (см. сабж) на С++ с хорошими примерами.


Название: Re: Dependency injection
Отправлено: once_again_abc от Август 28, 2014, 02:58
такой хороший форум, а программистов на С++ нет  >:( :-[ :-\ :'(


Название: Re: Dependency injection
Отправлено: Igors от Август 28, 2014, 07:30
Цитировать
Если, по вашему мнению, ответа долго нет, оскорбитесь, написав что-то в духе: я думал здесь умеют решать, а тут такие же бараны как я.
:)


Название: Re: Dependency injection
Отправлено: Bepec от Август 28, 2014, 07:48
http://vladimirsblog.com/laravel-dependency-injection-for-beginners/ не подойдёт?

Кстати я почитал, не до конца понял принцип. Получается мы отказываемся от ООП в пользу процедурного подхода для лучшей реализации в тестах?


Название: Re: Dependency injection
Отправлено: _Bers от Август 28, 2014, 22:48
http://vladimirsblog.com/laravel-dependency-injection-for-beginners/ не подойдёт?
Кстати я почитал, не до конца понял принцип. Получается мы отказываемся от ООП в пользу процедурного подхода для лучшей реализации в тестах?

Нет. Вы отказываетесь от практичного ООП в пользу ООП гловного мозга.

Иньекция зависимостей - анти-паттерн, как сделать простое более сложным.


Название: Re: Dependency injection
Отправлено: once_again_abc от Август 29, 2014, 02:13
http://vladimirsblog.com/laravel-dependency-injection-for-beginners/ не подойдёт?

Кстати я почитал, не до конца понял принцип. Получается мы отказываемся от ООП в пользу процедурного подхода для лучшей реализации в тестах?



вот поэтому я и прошу поделиться информацией - чтобы знать ответы на такие вопросы. знаю что DI штука очень мощная и полезная, особенно в языках, где есть сборщик мусора. знать и применять этот супермегапаттерн is a must для любого нормального инженера.


Название: Re: Dependency injection
Отправлено: Bepec от Август 29, 2014, 08:06
Насколько мне видится тут наоборот идёт странное влечение от простого к сложному + разбрасываем сущности налево направо. Ну да ладно, надеюсь когда вы доберётесь до истины, вы поведаетё и нам.


Название: Re: Dependency injection
Отправлено: navrocky от Август 30, 2014, 15:26
Читнул по диагонали, в общем-то обычный код, я так и пишу. Теперь буду знать, что это оказывается ПАТТЕРН, ёпта.

То есть там сперва приводят пример быдлокода, потом нормальный, типа паттерн применили.

Ну да ладно.


Название: Re: Dependency injection
Отправлено: Bepec от Август 30, 2014, 15:54
На мой взгляд бессмысленное раскидывание сущностей или неудачный пример.

Если нужен метод только сохраняющий новую книгу в одну бд - то зачем его менять? Вот он, создаёт новую книгу и пихает в базу.

Конечно если мы рассчитываем на дальнейшее, можно строить архитектуру, разбросать на базу/драйвер/сохранение/книгу/заглавие/главы/строчки. Но просто так это делать?

Было
Код:
$bookStorage = new BookStorage;
$bookStorage->store('Фёдор Михайлович Достоевский', 'Идиот');

Стало
Код:
$book = new Book;
$book->author = 'Фёдор Михайлович Достоевский';
$book->title = 'Идиот';

$database = new Database;

$bookStorage = new BookStorage($database);
$bookStorage->store($book);

На мой взгляд это головная боль дальнейшей разработки, весь контроль ложится на программиста, плодятся кучи сущностей. А если классов не 3, а 15?
Вместо читабельного кода появляется мусорная куча. Добавляем обработку ошибок и... И всё ещё хуже, ибо обработку тоже напишем в этом стиле, то вместо 2 строк получим 100500.

Инкапсуляция должна быть.


Название: Re: Dependency injection
Отправлено: vregess от Август 30, 2014, 16:01
Можешь посмотреть
http://adam.younglogic.com/2008/07/dependency-injection-in-c/ (http://adam.younglogic.com/2008/07/dependency-injection-in-c/)
http://stackoverflow.com/questions/4469304/dependency-injection-framework-for-c (http://stackoverflow.com/questions/4469304/dependency-injection-framework-for-c) (включает в себя первую ссылку).

Я имел дело с DI лишь однажды на нескольких AS3 проектах, использовал фреймворк robotlegs (и, возможно, еще на java с ее guice). Да, вещь удобная при определенных условиях.
В первом приближении можно рассматривать DI как фабрику объектов, которая разруливает зависимости создаваемых объектов. Можно удобно менять реализации (на пример, для тестов использовать другие классы/объекты). Все это можно сделать руками, ну а тут как бы паттерн, и ты уже не просто инженер, а "нормальный".

После as3 проектов искал что-то похожее и на с++, помню первую ссылку и читал или что-то сильно похожее. Даже пробовал использовать. В результате отказался, т.к. мне показалось что нормальная реализация DI, которая не усложнит проект, возможна только при вменяемой поддержке интроспекции (https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D1%80%D0%BE%D1%81%D0%BF%D0%B5%D0%BA%D1%86%D0%B8%D1%8F_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29ъ) в языке. Навроде java (там есть Guice) или питона. Ну а в с++, где с этим проблемы, можно наворотить такого, что потом не разгребешь.

Причем тут сборщик мусора, не понял.

В общем в мире с++ реализации есть, но они не на столько хороши, как в других языках, и пока вряд ли будут.

PS to Верес.
весь контроль ложится на программиста

Как раз наоборот, все за тебя сделает DI фреймворк, и никаких раскидываний сущностей. Пример и правда не особо.

Если есть прям не преодолимое желание разобраться с dependency injection, то лучше смотреть реализации для других языков (java, as3, c#), где есть нормальные фреймворки.


Название: Re: Dependency injection
Отправлено: _Bers от Август 30, 2014, 17:09
На мой взгляд бессмысленное раскидывание сущностей или неудачный пример.

Первое.
Скажем так: удачных примеров я почему то ни разу не наблюдал.
Адепты о них почему то молчат. Подозреваю - сами не знают.


На мой взгляд это головная боль дальнейшей разработки, весь контроль ложится на программиста, плодятся кучи сущностей. А если классов не 3, а 15?
Вместо читабельного кода появляется мусорная куча. Добавляем обработку ошибок и... И всё ещё хуже, ибо обработку тоже напишем в этом стиле, то вместо 2 строк получим 100500.

Инкапсуляция должна быть.

О том и речь: ооп ради ооп есть ооп головного мозга.

Фишка этого анти-паттерна в том, что вы можете встретить кучку людей, заявляющих "это круто". Но ни один из них не сможет привести ни одного реального примера, который иллюстрирует профит.



Название: Re: Dependency injection
Отправлено: Bepec от Август 30, 2014, 17:36
А я верил что оно полезное. Даже гуглил :/ Какое разочарование.

to ck: Я вижу вы в теме, давайте поговорим.

1) Основная суть паттерна - раскидывание задачи на подзадачи + возможность на место любого объекта подставить другой, так?
2) Это решается просто зависимостями класса. Т.е. вместо
Код:
$book = new Book;
$book->author = 'Фёдор Михайлович Достоевский';
$book->title = 'Идиот';

$database = new Database;

$bookStorage = new BookStorage($database);
$bookStorage->store($book);
можно спокойно написать
Код:
book::book(string inAuthor, string inTitle, BookStorage * inStore = 0, Database * inBase = 0)
,author(inAuthor)
,title(inTitle)
{
store = inStore;
base = inBase;
}

И в результате получаем ту же зависимость по смыслу, но более ООП-шную и разруливаемую классом. Разница на мой взгляд просто в порядке инициализации.

В чем преимущество паттерна? В чем плюсы?


update: написал, прочитал и понял что единственно - мы фиксируем архитектуру без этого паттерна. Т.е. зависимость прямая. Но это несущественно.

Судя по всему неудачность именно в примере. А паттерн просто неразрывно связан с ООП, т.е. имеет ту же цель - разбиение на подзадачи. Таким образом он скорее часть ООП как паттерна программирования.


Название: Re: Dependency injection
Отправлено: vregess от Август 31, 2014, 18:59
1) Основная суть паттерна - раскидывание задачи на подзадачи + возможность на место любого объекта подставить другой, так?

Не так. Его задача сделать классы менее связанными друг с другом (как и многих других паттернов), чтобы использующий класс, не отвлекался на создание используемого класса.

В твоем примере основная идея не в классе Book, он тут как некие данные, которыми просто оперируют. Все начинается там, где появляется Database. Изначально мы имеем (сокращенный пример из того поста):

Код
PHP
class BookStorage
{
   private $db;
   public function __construct()
   {
       $this->db = new Database;
       // or
       //$this->db = Database::getInstance()
   }
 
   public function store(Book $book)
   {
       $this->db->insert(book);
   }
}
 
 

Здесь BookStorage зависит от Database, тк явно создает его в своем конструкторе. Из-за этого мы не сможем поменять Database на другую реализацию. Поэтому мы добавляем аргумент в конструктор, чтобы передать уже сконструированный Database:

Код
PHP
class BookStorage
{
   private $db;
   public function __construct(Database db)
   {
       $this->db = db;
   }
 
   public function store(Book $book)
   {
       $this->db->insert(book);
   }
}
 

Вуаля. Мы сделали внедрение зависимости. За громким названием скрывается достаточно простой принцип. Мы "засовываем" один объект внутрь другого, внедряем его. Мы внедряем объект, от которого зависит BookStorage.

Получаем, классу BookStorage больше не нужно создавать Database самостоятельно, он просто им пользуется, и мы легко можем менять реализации Database. Надеюсь, разжевывать преимущества второго подхода над первым не нужно. Вот об этом и был тот пост. Он просто проиллюстрировал один из вариантов внедрения зависимости (внедрение зависимости через конструктор).

Где здесь анти-паттерн?

Это что касается того конкретного примера, по ссылке Вереса, который не понятно почему был приведен, наверное первая попавшаяся ссылка. Ну да ладно.

Разберемся по-быстрому с инверсией управления (Inversion of Control, IoC).
По названию совсем не ясно, что за этом стоит, по крайней мере для меня, не ясно, зачем так назвали. Цель - тоже уменьшить связанность между классами (сущностями). На примере BookStorage и Database. Здесь Database - это конкретная реализация хранилища, Получаем, что BookStorage может работать только с базой данных. BookStorage тесно связан с базой данных, как хранилищем. Сейчас мы поменяем зависимости путем введения новой сущности - интерфейс хранилища. Пример вперемешку с с++, т.к. я php не знаю.

Код
PHP
class IStorage {
   virtual void insert(Book);
}
 
class Database: public IStorage {
   virtual void insert(Book) {...}
}
 
class BookStorage
{
   private $db;
   public function __construct(IStorage db)
   {
       $this->db = db;
   }
 
   public function store(Book $book)
   {
       $this->db->insert(book);
   }
}
 

Теперь BookStorage зависит от абстракции IStorage, а не от конкретной реализации. Этот подход и называют инверсия управления.

Теперь у нас есть 2 подхода - внедрение зависимости и инверсия управления.
Используя их мы можем создать так называемый IoC-контейнер, который по сути является фабрикой объектов (фабрика - опять не совсем понятное название паттерна, пусть будет Создаватель)
и содержит в себе знания, как эти объекты создавать и разруливать зависимости, если надо. Изначально мы создаем конфигурации для разных объектов, а потом по запросу IoC контейнер нам создает объекты.

Навроде:
Код
C++ (Qt)
ioc.forClass(IStorage).create(Database);
 

Пример с зависимостями может выглядеть как:

Код
C++ (Qt)
class Database {
   Database(One a, Two b) { ... }
   ...
}
 
ioc.forClass(IStorage).create(Database);
ioc.forClass(One).create(OneStuff);
ioc.forClass(Two).create(AnotherStuff);
 

Мы не указываем, как создавать Database, IoC сам все разрулит. Либо конфигурация задается отдельным файлом типа XML.
Далее мы просим у IoC создать для нас объекты:
Код
C++ (Qt)
db = ioc.get(IStorage)
 

В некоторых реализациях процесс запросов к IoC-контейнеру происходит автоматически. На пример, в robotlegs мы помечаем атрибут класса, и ему присваивается значение автоматически.

Код
C++ (Qt)
class Bla {
   [Inject]
   public Database storage;
 
   ...
   storage.insert(book);
}
 

Все это похоже на фабрику объектов, разница в том, что фабрики ты пишешь сам, а тут делается за тебя.
Хорошо сказано тут (http://stackoverflow.com/questions/557742/dependency-injection-vs-factory-pattern):
Цитировать
When using a factory your code is still actually responsible for creating
objects. By DI you outsource that responsibility to another class or a
framework, which is separate from your code.
(Переводим самостоятельно).

Вот в принципе так, вкратце.

Для закрепления основ можно почитать серию постов:
http://www.apofig.com/2010/08/dependency-injection-inversion-of.html (http://www.apofig.com/2010/08/dependency-injection-inversion-of.html)
http://www.apofig.com/2011/07/dependency-injection-inversion-of.html (http://www.apofig.com/2011/07/dependency-injection-inversion-of.html)
http://www.apofig.com/2011/07/dependency-injection-inversion-of_13.html (http://www.apofig.com/2011/07/dependency-injection-inversion-of_13.html)
http://www.apofig.com/2011/07/dependency-injection-inversion-of_23.html (http://www.apofig.com/2011/07/dependency-injection-inversion-of_23.html)
http://www.apofig.com/2011/10/dependency-injection-inversion-of.html (http://www.apofig.com/2011/10/dependency-injection-inversion-of.html)
http://www.apofig.com/2012/03/java-for-fun-dependency-injection.html (http://www.apofig.com/2012/03/java-for-fun-dependency-injection.html)

примеры на java, но разберешься - тыжпрограммист.





Название: Re: Dependency injection
Отправлено: Bepec от Август 31, 2014, 19:39
Хотя в принципе неважно, принцип я уловил.

Кратко: мы перекладываем контроль над модулями в левый класс, создавая прослойку с собственными параметрами.

Таким образом мы увеличиваем гибкость системы, но получаем в нагрузку перегруженный класс, который знает всё о всех.
Гибкость ещё бОльшая, чем можно добиться в классе, но усложняет структуру на порядок.

В принципе обычная фабрика, только не классов, а модулей для классов.

Так то плюсов не видно, минусы - класс всё обо всём.

Печально и неоднозначно.



Название: Re: Dependency injection
Отправлено: vregess от Сентябрь 01, 2014, 06:01
Похоже суть ты не особо уловил, но раз в принципе не важно, то  и не важно.
Если нет понимания, как и где применять инструмент, то лучше его не использовать.
Просто обходи его стороной, тем более на с++.


Название: Re: Dependency injection
Отправлено: once_again_abc от Сентябрь 01, 2014, 06:47
...


Спасибо за ссылки и особенное спасибо за отличное объяснение DI и IoC!


Название: Re: Dependency injection
Отправлено: Bepec от Сентябрь 01, 2014, 08:39
Угу :) Самое важное упущение - нет практического применения. Точнее я его не вижу, мб потом найдётся задачка :)

В любом случае спасибо за объяснение.

PS но если сложное нельзя объяснить простыми словами - то либо неполное знание предмета, либо предмет не имеет четкой сформулированности и он больше как "теория".


Название: Re: Dependency injection
Отправлено: _Bers от Сентябрь 01, 2014, 13:45
Вуаля. Мы сделали внедрение зависимости. За громким названием скрывается достаточно простой принцип. Мы "засовываем" один объект внутрь другого, внедряем его. Мы внедряем объект, от которого зависит BookStorage.

Получаем, классу BookStorage больше не нужно создавать Database самостоятельно, он просто им пользуется, и мы легко можем менять реализации Database. Надеюсь, разжевывать преимущества второго подхода над первым не нужно. Вот об этом и был тот пост. Он просто проиллюстрировал один из вариантов внедрения зависимости (внедрение зависимости через конструктор).

Где здесь анти-паттерн?

Анти-паттерн в том, что класс BookStorage в этом случае вообще становится не нужным.
ООП головного мозга такое ООП - плодите кучу бесполезной ботвы, к тому же вредной - усложнение конструкции за счет непомерного раздувания оо-архитектуры кучей ненужных и избыточных сущностей.

БД изначально была деталью реализации механизма, которая специально для того и была инкапсулирована, что бы мы (пользователи) могли выполнять операции над книжкой, и ни о чем не париться.

Вы взяли и вытащили кишки наружу. Теперь пользователям самим нужно геммороиться по созданию объекта БД, его и его настройке.
Но если так: тогда накой черт вообще нужен класс BookStorage?

Принцип действия этого анти-паттерна можно выразить словами: давайте нарушим инкапсуляции, и вытащим кишки класса наружу. И предложим пользователям самостоятельно поиметь геммор со всем этим.


зы:

Чем то мне это напоминает паттерн "стратегия".

Но у "стратегии" есть своя четко очерченная область применения.
И "стратегия" действительно имеет профит.




Название: Re: Dependency injection
Отправлено: once_again_abc от Сентябрь 01, 2014, 14:04

Анти-паттерн в том, что класс BookStorage в этом случае вообще становится не нужным.
ООП головного мозга такое ООП - плодите кучу бесполезной ботвы, к тому же вредной - усложнение конструкции за счет непомерного раздувания оо-архитектуры кучей ненужных и избыточных сущностей.


такая иррациональная упертость сродни религиозному фанатизму.


Название: Re: Dependency injection
Отправлено: Bepec от Сентябрь 01, 2014, 14:19
Ну так поясните упёртым и непонимающим, в чем смысл? В чем плюс? В чем потенциал паттерна?

Пример: паттерн model-View позволяет разделить данные и их отображение. Польза? Польза. Можно отображать как хочешь и что хочешь, не меняя модель.

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

ООП обычное - класс "машина" с 4 слотами под класс "колёса" с унифицированным интерфейсом.
Получаем "Любая машина с любыми колёсами".

С DI - класс прослойка, который знает всё о классах машины, колёс, знает принципы их взаимодействия, класс машина с 4 слотами под класс "колёса" с унифицированным интерфейсом.
Получаем "Любая машина с любыми колёсами" и прослойкой. Смысл?

PS специально выделил цветом, что одинаково в обоих случаях.


Название: Re: Dependency injection
Отправлено: _Bers от Сентябрь 01, 2014, 18:42
такая иррациональная упертость сродни религиозному фанатизму.

Как насчет заменить фразу "религиозный фанатизм" на фразу "здравый смысл" ?



Название: Re: Dependency injection
Отправлено: vregess от Сентябрь 01, 2014, 22:14
БД изначально была деталью реализации механизма, которая специально для того и была инкапсулирована, что бы мы (пользователи) могли выполнять операции над книжкой, и ни о чем не париться.

Что-то ты напридумывал. Судя по коду, инкапсулирована она была для получения доступа к ней же из BookStorage, чтобы записать туда book. Откуда ты взял все остальное не ясно.

Вы взяли и вытащили кишки наружу. Теперь пользователям самим нужно геммороиться по созданию объекта БД, его и его настройке.

Это не кишки, вот если бы я вытащил наружу сожедримое метода insert(), то да. Создание Database не относится к внутренностям класса BookStorage, это не его ответственность, о чем, кстати, говорит само название класса BookStorage - Storage. Где тут про базы данных? Вот по этому-то и вытаскивают Database, чтобы избавить BookStorage от не относящихся к нему операций (создание Database).

Принцип действия этого анти-паттерна можно выразить словами: давайте нарушим инкапсуляции, и вытащим кишки класса наружу. И предложим пользователям самостоятельно поиметь геммор со всем этим.
Инкапсуляция тут совсем не нарушается - данные остаются с методами, их обрабатывающими (метод insert), а вот создание связанных сущностей выносится во вне. Причем это делается с определенной целью, а не посто ради создания дополнительных сущностей. Это не ООП ради ООП.

Твоя главная ошибка здесь, что ты считаешь бд и ее создание неотъемлемой частью класса BookStorage. Это первая причина разногласий.
А на самом деле мы провели рефакторинг и оптимизировали класс BookStorage, оставив ему только функции, связанные с манипуляциями хранилищем.

А для оставшегося функционала (создание и внедрение в BookStorage) у нас есть несколько вариантов:
1. Отдаем на откуп пользователя - об этом ты писал. Заметь, до этого момента
нет ничего противозаконного, никаких анти-паттернов. Просто ты не видишь
преимуществ "отрефакторенного" BookStorage, или не хочешь видить, тк уверен, что
все это какой-то мифический анти-паттерн.
Тут мы сами создаем и  сами внедряем бд в BookStorage.
Некоторые преимущества см. в пункте 2.

2. Помещаем создание в фабрику или что-то похожее - теперь создает не пользователь,
а фабрика, но внедряет все еще пользователь. Тут создается "лишняя" сущность. Будем считать
это отрицательным моментом, но оно приносит ряд преимуществ: легче сопровождать
BookStorage, легче заменять одни части на другие, лугче расширять код,
повторное исользование компонентов становится легче.
Получается для тебя это куча бесполезной ботвы, да еще и вредной. Единственное, что
можно вытащить из твоей цитаты, так это "усложнение конструкции", да и то спорное.

3. Отдаем на откуп IoC-контейнера - создание и внедрение происходит автоматически.

4. Что-нибудь еще, выходящее за рамки поста.

класс BookStorage в этом случае вообще становится не нужным.
Это не так, потому что BookStorage продолжает выполнять свою функцию - сохраняет данные в хранилище.


резюме:
1. То, что мы вытащили, не является "кишками" BookStorage, просто этого там не должно быть (в данном случае).

2. Это совсем не бесполезное, и даже полезное (в данном случае), потому что уже само по себе приносит ряд улучшений.

3. Это не ООП ради ООП, все это для создания меньшей связанности между классами. Конечно отвязать все отовсюду это перебор, и уже будет раздуванием архитектуры, но совсем не в нашем случае.

4. Ты выдираешь фразы из контекста.
Вот мы мы вытащили создание бд из класса и поместили в еще один класс. Теперь у нас 2 класса.
Ты берешь эти 2 факта (вытащили что-то и добавили еще один класс) и на нах строишь свою логику, совсем не учитывая, зачем это было сделано.
Конечно, в таком случае, все эти дейтвия будут казаться странными.
Разберись, для чего это было сделано.

А я еще раз повторю, что это все в основном для уменьшения связанности компонентов. А потом поищи, зачем компоненты стремятся отвязать друг от друга.

Если ты пишешь hello-world, то DI/IoC для тебя будет излишним, и ты получишь свое "раздувание оо-архитектуры кучей ненужных и избыточных сущностей".
Хотя даже в маленьких проектах ты не желая того делаешь инверсию управления, выделяя интерфейс из класса (а это потому-что паттерны проектирования всего лишь систематизация общеупотребляемых практик).

Ну а еще есть не hello-world проекты, где это успешно можно применить.


в чем смысл? В чем плюс?
Еще раз, слабая связанность, избавление от ручного создания объектов.

внедрения прослойки (опять таки внедрённой только ради внедрения)
Прослойка внедряется для того, чтобы отвязать 2 сущности друго от друга и убрать из них лишнюю нагрузку в виде создания определенных объектов.


класс прослойка, который знает всё о классах машины, колёс, знает принципы их взаимодействия
Что за панический страх перед прослойками?
Я тебе по секрету скажу, что есть опять же паттерн фасад. И он специально нужен, чтобы знать обо всех, и он - прослойка.

Вот почему класс-прослойка у тебя вызывает вопросы, а прослойка между монитором и стулом не вызывает?
Почему такая избирательность.


Название: Re: Dependency injection
Отправлено: _Bers от Сентябрь 01, 2014, 22:47

Что-то ты напридумывал. Судя по коду, инкапсулирована она была для получения доступа к ней же из BookStorage, чтобы записать туда book. Откуда ты взял все остальное не ясно.

Судя по коду - она была инкапсулирована. Это все что я знал судя по коду.
Другими словами, пока вы не нарушили инкапсуляцию и не вытащили кишки - я вообще не знал ни о какой БД.

И имел полное право ничего о ней не знать.
Мне не нужно вникать в детали реализации класса только для того, что бы пользоваться его услугами посредством его публичного интерфейса.

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

А теперь ответьте мне на мой вопрос: Если вы вытащили кишки класса наружу, и свалили мне на голову свою БД, накой чорт мне тогда может быть нужен класс BookStorage?

BookStorage мне был нужен именно для того, что бы простую операцию сделать просто: ничего при этом не зная ни о каких бд с их особенностями.

Но если уж вашей милостью мне все равно пришлось иметь дело с бд, тогда накой чорт мне нужен BookStorage?
ведь все, что он может сделать используя бд, и я сам могу сделать используя бд.

Вы усложнили жизнь мне, и одновременно лишили смысла существование класса BookStorage.

----------

Понимаете, ваши стремные зависимости - это ни что иное, как детальки реализации механизма, которые вы вытащили на уровень пользователя.

По хорошему, реализацию нужно прятать в приватах подальше от пользователей, что бы не утруждать последних никакими излишними размышлениями.

Чем меньше нужно знать, что бы полноценно пользоваться - тем проще. А чем проще - тем лучше. Всегда.
В этом заключается суть контроля за сложностью проекта: нужно минимизировать количество знаний, нужных для того, что бы пользоваться механизмом.

А не вытаскивать сор из избы ---> говнокод наружу ---> детали реализации за пределы класса.



Название: Re: Dependency injection
Отправлено: _Bers от Сентябрь 01, 2014, 22:50
в чем смысл? В чем плюс?
Прослойка внедряется для того, чтобы отвязать 2 сущности друго от друга и убрать из них лишнюю нагрузку в виде создания определенных объектов.

Любую проблему можно решить внедрением ещё одного слоя абстракции. Кроме проблемы избыточных слоёв абстракций.


Название: Re: Dependency injection
Отправлено: Old от Сентябрь 01, 2014, 23:00
2_Bers. А если я хочу, что класс BookStorage в качестве бекенда мог использовал разные хранилища? Например, в MySql базу или xml файл, а может в json или обычный текстовый файл?
Для чего мне прятать создание базы внутри, я могу создавать бекенды и подсовывать их BookStorage. А BookStorage зная только интерфейс сможет сохранять информацию о книгах куда мне надо.


Название: Re: Dependency injection
Отправлено: _Bers от Сентябрь 01, 2014, 23:13
2_Bers. А если я хочу, что класс BookStorage в качестве бекенда мог использовал разные хранилища? Например, в MySql базу или xml файл, а может в json или обычный текстовый файл?
Для чего мне прятать создание базы внутри, я могу создавать бекенды и подсовывать их BookStorage. А BookStorage зная только интерфейс сможет сохранять информацию о книгах куда мне надо.

Для того, что вы хотите bookstorage не нужен:

Код:
book b;
serialize::xml(b);
serialize::json(b);



Название: Re: Dependency injection
Отправлено: Old от Сентябрь 01, 2014, 23:21
А вы шире на это посмотрите. Я воспользовался классами из обсуждения только для примера.
Бывает полезно создавать объект руками и отдавать на использование другому объекту. Возможностей по созданию и настройке больше.


Название: Re: Dependency injection
Отправлено: _Bers от Сентябрь 01, 2014, 23:37
А вы шире на это посмотрите. Я воспользовался классами из обсуждения только для примера.
Бывает полезно создавать объект руками и отдавать на использование другому объекту. Возможностей по созданию и настройке больше.

Если глянуть шире - иногда это действительно имеет профит. Паттерн "стратегия" опять таки.
Но "стратегия" имеет четко обозначенную область применения, и не вызывает вопросов.

В отличие от "внедрения зависимостей", адепты которой сами толком не могут по простому ответить на вопрос - зачем это нужно.

зы: надеюсь про "возможности по созданию и настройкам" - это вы не всерьёз.


Название: Re: Dependency injection
Отправлено: Old от Сентябрь 01, 2014, 23:50
Почему не всерьёз - всерьёз.
Если мы спрячим объект базы внутри BookStorage, то не создать, не настроить этот объект мы не сможем.

Кстати, в чем вы видите разницу между стратегиями и этим DI?


Название: Re: Dependency injection
Отправлено: _Bers от Сентябрь 02, 2014, 00:12
Почему не всерьёз - всерьёз.
Если мы спрячим объект базы внутри BookStorage, то не создать, не настроить этот объект мы не сможем.

Я к тому, что настройки должны быть максимально простыми.
Не нужно ни "гибкости", ни "возможностей". Нужна простота.


Пример:
Взять обычный std::vector.

Им пользуются многие программисты, в том числе даже и новички.

Но многих ли вы знаете из тех, кто хоть раз применял "тонкие настройки" - замену стандартного аллокатора на свой, кастомный?

Я знаю массу случаев, когда действительно это было нужно, и люди предпочитали заменить весь контейнер целиком собственным велосипедом, лишь бы не долбаться...


Итого:
практика показывает, что:

1. Простой в эксплуатации механизм пользуется спросом.
2. Сложный в эксплуатации механизм не пользуется спросом.


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

Кстати, в чем вы видите разницу между стратегиями и этим DI?

Область применения стратегии: необходимость изменить поведение объекта в рантайме.

Например, мы конструируем модель мира - полигон, где живут животные.

Волки зимой должны сбиваться в стаю, а летом - охотиться в одиночку.

Это не просто один и тот же класс животного.
Это - один и тот же экземпляр животного.
И в зависимости от некоторых условий, происходящих в рантайме, экземпляр должен менять собственное поведение.

---

Что касается DI - я так и не понял, что это такое, и зачем оно нужно.
Все, что я о нем знаю сейчас - что это анти-паттерн, который только все усложняет.
Потому что все то, что можно сделать с помощью DI, можно сделать и без DI, только проще.


Название: Re: Dependency injection
Отправлено: Bepec от Сентябрь 02, 2014, 00:40
Почитал, впечатлился. В хорошем смысле этого слова.

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

Я пока не вижу задач, ради которых надо вытаскивать из класса нутро.

Возьмём Qt (аки форум всё таки по Qt). QSqlDatabase работает с БД. Через QSqlDriver. А QSqlDriver имеет различные реализации для БД. А вот для удобной инициализации у QSqlDatabase имеется статический метод, вытаскивающий драйвер и создающий нужный нам QSqlDatabase.

Слабая связанность? да.
Простота? да.
Легко в отладке? да.
Переписать под себя? легко.
DI в слабой форме - всё таки да, думаю это DI. А то, что вы показываете нам - больше похоже на преувеличение.

Приведите задачу. Пожалуйста. Которая вот плохо реализуема, а вот с DI легко и незатейливо.

update: солидарен с _Bers - такие вещи делаются проще и без DI. Всё равно нам надо прописывать интерфейсы классов, необходимо прописывать зависимости. Обычных классом лучше,потому что там действует принцип инкапсуляции. Когда ты в душе не чаешь что кроется за "QString.replace("\n\r", "haha");", но оно работает. И ты туда присунуть можешь всё, начиная от адреса памяти, заканчивая TCHAR.


Название: Re: Dependency injection
Отправлено: Igors от Сентябрь 02, 2014, 08:00
Код
PHP
class BookStorage
{
   private $db;
   public function __construct(Database db)
   {
       $this->db = db;
   }
 
   public function store(Book $book)
   {
       $this->db->insert(book);
   }
}
 

Вуаля. Мы сделали внедрение зависимости. За громким названием скрывается достаточно простой принцип. Мы "засовываем" один объект внутрь другого, внедряем его. Мы внедряем объект, от которого зависит BookStorage.
Это не вызывает вопросов, но причем здесь "инжекция" - хз. Вот напр метод insert платфомо-зависимый - и мы вынуждены так делать. Или хотим иметь возможность работать с разными типами БД. Ясно что реализацию надо изолировать от клиента.  Тогда чем плохи стандартные подходы - виртуальный базовый класс и/или фабрика?


Название: Re: Dependency injection
Отправлено: Old от Сентябрь 02, 2014, 08:40
Я к тому, что настройки должны быть максимально простыми.
А я и не говорю о каких-то сложных настройках:
Код
C++ (Qt)
{
   StorageBackend *stor = cmdLine.contains( "--mysql" )?
                                           MySqlStorage( host, port, user, password ) :
                                           XmlStorage( filename, "UTF-8" );
 
   BookStorage book( stor );
}
 
Настройки просты, но их нужно задать.

Что касается DI - я так и не понял, что это такое, и зачем оно нужно.
Это потому, что он ничем не отличается от стратегии, ну кроме названия.
И в одном и в другом подходе, мы выносим неких функционал из класса, для того что-бы иметь возможность управлять им отдельно.


Название: Re: Dependency injection
Отправлено: Bepec от Сентябрь 02, 2014, 11:38
Т.е. моё утверждение, что это лишь часть паттерна ООП, неизвестно кем выдвинутая в отельный паттерн верно? :)


Название: Re: Dependency injection
Отправлено: vregess от Сентябрь 02, 2014, 21:00
А теперь ответьте мне на мой вопрос: Если вы вытащили кишки класса наружу, и свалили мне на голову свою БД, накой чорт мне тогда может быть нужен класс BookStorage?
Так ведь он нужен для операций с хранилищем. Ну добавим в метод insert() еще пару строк, а потом добавим еще метод remove(). И что, ты каждый раз теперь будешь дублировать операции из insert() и remove(), раз смысл в BookStorage пропал? BookStorage теперь содержит только операции с хранилищем и избавлен от разруливания зависимостей при создании этого хранилища.
Да и почему свалили тебе на голову БД, ведь вытаскивание этих "кишок" всего лишь подготовка класса для работы с IoC контейнером. Эти "кишки" затем опять будут спрятаны, но уже в другом месте.

Вот еще одно определение для DI:
Цитировать
Внедрение зависимостей — паттерн проектирования, основная задача которого — отделить поведения объекта от управления его зависимостями

Еще раз повторю, эти "кишки" достаются не тебе, а перемещаются в другую сущность.

Но если уж вашей милостью мне все равно пришлось иметь дело с бд, тогда накой чорт мне нужен BookStorage?
ведь все, что он может сделать используя бд, и я сам могу сделать используя бд.

Да, можешь и сам, но если тебе понадобится сделать это в нескольких местах, ты все-равно создашь отдельных класс, чтобы не дублировать код.
Вот за этим он и нужен, инкапсулирует операции над хранилищем. Да, в этом примере из двух классов все это не имеет особого смысла, но ведь это только пример, чтобы показать принцип.


В отличие от "внедрения зависимостей", адепты которой сами толком не могут по простому ответить на вопрос - зачем это нужно.
Так ведь в который раз уже: уменьшить связанность между компонентами, избавление от ручного создания объектов; отделение поведения объекта от управления его зависимостями.

Кстати, в чем вы видите разницу между стратегиями и этим DI?

Все же DI и компания ближе к фабрикам, чем к стратегиям.

Я имел возможность убедиться в этом на собственной шкуре - когда однажды понял, что совершенно напрасно потратил время и усилия на поддержку "тонких настроек", которыми никто никогда так и не воспользовался, потому что "это гимморно слишком, проще взять другой механизм, который из коробки лучше отвечает требованиям".
Ну вот был у тебя неудачный опыт с избыточными сущностями, причем не с DI/IoC, а все-равно это анти-паттерн, причем потом еще оказывается, что

Что касается DI - я так и не понял, что это такое, и зачем оно нужно.
Откуда взялось стойкое мнение, про абсолютную вредность этого паттерна, если нет ясного понимания? Меня вот это удивляет.
Верес тоже хорош, привел ссылку и тут же говорит, что не понял, что написано. Так зачем было вообще ее постить?  Увидел слова "внедрение" и "зависомость"? Так может там описание порнофильма с наркоманами было, а не про DI?

Зачем вводить людей в заблуждение. Это вполне нормальный прием, который удачно можно использовать.

Это не вызывает вопросов, но причем здесь "инжекция" - хз.
Ну как причем, показан прием инъекции через конструктор.

Тогда чем плохи стандартные подходы - виртуальный базовый класс и/или фабрика?
Все, что связано с DI/IoC, относится не к изменению поведения, а к переносу ответственности за создание объектов в другое место. Фабрика здесь вполне подойдет, но об отличии фабрики от ID я выше приводил цитату с SO.

Может быть я что-то не так объяснил, раз народ до сих пор чешет репу, зачем это нужно (а может вам оно и не нужно?). Ну давайте вот еще один пример, уже из документации Guice (java): https://github.com/google/guice/wiki/Motivation (https://github.com/google/guice/wiki/Motivation) (англ).

PS я не призываю никого использовать этот паттерн, и не доказываю, что это какая-то панацея от всех бед, что он лучше других подходов. Единственное, что хочется донести, что его называют бесполезным и опасным совсем не заслуженно.


Название: Re: Dependency injection
Отправлено: Bepec от Сентябрь 02, 2014, 23:07
Цитировать
Верес тоже хорош, привел ссылку и тут же говорит, что не понял, что написано. Так зачем было вообще ее постить?  Увидел слова "внедрение" и "зависомость"? Так может там описание порнофильма с наркоманами было, а не про DI?
Вот поэтому и спросил ) Искал незнаю что, нашёл ссылку, попал пальцем в небо и спросил знающих :)
Я прочитал, попытался осознать и не понял. Попросил объяснить. Найдёте в этом поведение предъосудительное - пишите, побеседуем :D

Прочитал по вашей ссылке, там действительно разложено лучше, и понял - вы мешаете DI, фабрики и приписываете чудодейственные свойства этой смеси :D

Вот DI. Настоящий и без затей.
Код:
QNetworkReply *	post(const QNetworkRequest & request, QIODevice * data)
Проще выражаясь - метод не создаёт QNetworkRequest и... И всё :)
Он имеет зависимость от QNetworkRequest, при этом не управляя его созданием.  

Зачем нужен этот паттерн в практике - для упрощения написания тестов.

Почему многие не могли его понять по вашим объяснениям - вы мешаете всё в кучу. Фабрика остаётся при этом фабрикой. Она в DI не нужна. Её как и любой другой объект можно использовать, но даёт лишнюю сущность и глобальный объект "знающий всех".  :)

Теперь я доволен и это похоже на правду. Правда, т.к. этот паттерн в с++ используется часто, он стал для меня обыденностью :)

PS полезность его находится в заоблачной высоте.


Название: Re: Dependency injection
Отправлено: vregess от Сентябрь 03, 2014, 05:19
Он имеет зависимость от QNetworkRequest, при этом не управляя его созданием.  
Да, ты прав, все так и есть.

вы мешаете DI, фабрики и приписываете чудодейственные свойства этой смеси
...
вы мешаете всё в кучу. Фабрика остаётся при этом фабрикой. Она в DI не нужна
И тут скорее всего ты тоже прав, частично. Наверное лучше было не  упоминать про фабрики, при описании DI, это больше к IoC относится (реализация IoC есть в шестой части в серии постов, которые я в самом начале приводил). Запутал народ получается. Просто DI можно реализовать при помощи фабрик, это один из вариантов.
Вот тут есть про DI - fabric http://stackoverflow.com/questions/557742/dependency-injection-vs-factory-pattern (http://stackoverflow.com/questions/557742/dependency-injection-vs-factory-pattern)

там действительно разложено лучше
Значит я плохая объяснялка.


Название: Re: Dependency injection
Отправлено: Bepec от Сентябрь 03, 2014, 07:03
Ну и хорошо, что всё хорошо кончилось. Спасибо тебе за то, что потратил на нас своё время :D

PS становитесь объяснялкой получше :D


Название: Re: Dependency injection
Отправлено: Igors от Сентябрь 03, 2014, 13:49
Значит я плохая объяснялка.
Вовсе нет, просто иногда слишком категоричны :) Мне ближе точка зрения _Bers - беда когда паттерн (любой) начинает применяться без оснований, просто потому что паттерн - это (якобы) хорошо. Очень быстро паттернист так задрочит код что работать с ним станет невозможно. Но все равно, спасибо за разъяснения. 


Название: Re: Dependency injection
Отправлено: vregess от Сентябрь 03, 2014, 17:19
Мне ближе точка зрения _Bers - беда когда паттерн (любой) начинает применяться без оснований, просто потому что паттерн - это (якобы) хорошо. Очень быстро паттернист так задрочит код что работать с ним станет невозможно.
Так я с этим согласен и никого не призывал использовать паттерны, просто пришлось ссылаться на общепринятые названия, может поэтому кажется что я "паттернист" или сую их везде.

Точка зрения _Bers была:
Цитата: _Bers
Иньекция зависимостей - анти-паттерн
Лишь с этим утверждением я не согласен. А обо всем остальном я вроде и не заикался.