Синглтон, антипаттерн и все-все-все

esase

Новичок
Позвольте мне суммировать знания полученные чтением этого топика:
-------------------------

1. Между DiC и Service Locator разницы по сути нет.
2. Dic становится Service Locator если мы инжектим этот контейнер в качестве зависимости в какой нить класс? Вместо того, чтобы указать более точные
зависимости верно??

Отсюда вопросы:
1. А если у класса не ясные зависимости что ему передавать? к примеру я делаю класс payment основанных на одном интерфейсе, но заранее я не могу сказать
к примеру, что класс 1 будет юзать класс request, а другой и request и какую нить модель для выбора доп. данных.
Как тут обойтись без SL? Проще передать ему контейнер и пусть он сам решает какие зависимости ему нужны. Как вариант можно использовать DI фреймворк который по четким зависимостям
на пример в конструкторе определит, что нужно подставить.

2. Service Locator анти патерн потому, что обычно инжектят как раз таки контейнер в какой нибудь из классов, отсюда и не понятно на самом деле какие у класса зависимости,
класс сам вытягивает нужные ему зависимости из контейнера. Более точно было бы описание зависимостей скажем через конструктор, но если этих зависимостей будет достаточно много
то конструктор разрастется
 

Вурдалак

Продвинутый новичок
1. Значит ты неправильно проектируешь интерфейсы.
2. Если конструктор содержит много зависимостей, то делай декомпозицию.
 

esase

Новичок
1. Значит ты неправильно проектируешь интерфейсы.
2. Если конструктор содержит много зависимостей, то делай декомпозицию.
пример:

interface payment {
public function getPaymentForm();
public function validatePayment();
}

к примеру этому классу нужен всего объект request чтобы получить POST/GET параметры и валидировать данные

class webMoney implements payment {
protected $request;

public function __construct(IRequest $request){
$this->request = $request;
}
public function getPaymentForm(){....}
public function validatePayment(){....}
}

здесь все отлично теперь сделаем другую реализацию пайментов:

class hardPayment implements payment {
protected $request;

public function __construct(IRequest $request, IHashUtility $hashUtility, IModel $model){
$this->request = $request;
$this->hashUtility = $hashUtility;
$this->model = $model;
}
public function getPaymentForm(){....}
public function validatePayment(){....}
}

Как видите во втором классе нужно доп. две зависимости, в последующих классах они могут меняться. Вопрос Вурдалаку, каким образом здесь можно улучшить дизайн (это к вопросу о неверном использовании интерфейсов) и как не использовать Service Locator для таких видов классов???
 

esase

Новичок
1) Что такое IHashUtility?
2) Что такое IModel?
Не важно, просто взят некий пример. В примере хотел показать, что классам даже основанным на одном и том же интерфейсе иногда необходимы разные зависимости для того, чтобы реализовать заявленный функционал.
 

fixxxer

К.О.
Партнер клуба
Важно. Есть зависимости на уровне приложения, а есть зависимости на уровне модуля.

И еще меня смущают имена.
 

esase

Новичок
1) Что такое IHashUtility?
2) Что такое IModel?

----
Это подсказки типов, мы можем передать в конструктор только объекты сделанные на основе перечисленных интерфейсов. К примеру зачем нам IHashUtility имено здесь??

есть ряд сервисов которые подписывают контрольную подпись md5. кто то sh1 не суть важна, я пишу один интерфейс для обоих методов, и инжектю нужный в данный класс. Т.е абстрагируюсь от метода подписи. Если пайменты сменят вид подписи то данный класс даже этого не узнает ему все равно. Он получит все равно нужный объект который будет сформирован где то выше. А во обще повторюсь не суть важно, что я сюда передаю. Уделите внимание словам:

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

fixxxer

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

До имен я докопался, потому что это похоже на I, подписанное к имени класса, а не на название интерфейса.

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

esase

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

До имен я докопался, потому что это похоже на I, подписанное к имени класса, а не на название интерфейса.

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

Ok пойду с другого конца :)

Выводы Service Locator vs Di
------------------------------

1. Service Locator === DiC (Dependency Injection Container)
2. DI используется для уменьшения связанности кода, лучшим примером будет когда мы не создаем экземпляры нужных нам объектов в некотором объекте
, а передаем их через конструкторы, сетеры или свойства. При таком подходе нам будет очень легко протестировать такие объекты путем встраивания в них мок объектов.
Подменяя тем самым стандартные реализации. Или же даже просто переключаться с одной реализации на другую (Передавался Файл кеш, стали передавать MemCache).


3. Dic (Ioc) контейнеры также используется для уменьшения связанности кода тем, что сами создают для нас объекты, внедряя в них зависимости. Разница между Service Locator и DiС
в том, что Dic используется на этапе бутстрапа, т.е создает контроллеры к примеру добавляя в них необходимые зависимости. Т.е Dic работает ка бы из вне. Но
если мы будем вместо прямых зависимостей внедрять DiC контейнер и будем сами запрашивать сервисы (т.е сами управлять нашими зависимостями) то DiC автоматически
уже будет называться - Service Locator.

---
Чем хорош Service Locator:
1. С любого участка кода можно вызвать любой сервис, который будет уже подготовлен к работе (Будет с внедренными зависимостями).
2. Это очень быстро на самом деле.

Чем так плох Service Locator:
1. Мы добавляем сами зависимость от локатора - (хотя здесь я особо не соглашусь, у нас один фиг есть зависимости если мы юзаем Di, просто они более явные).
2. Класс с внедренными контейнерами тяжело тестить. (Возможно....) Когда класс использует Di мы можем легко подменить зависимости моками.
3. Если захочешь найти вызываемый метод из Service Locator - запаришся (Возможно, но всегда ели есть четкое апи, т.е ты знаешь в каких местах заполняется этот
самый Service Manager).

А теперь самое интересное:
1. Почему если Service Locator так плох, то его юзают по всюду в крупных фреймворках (Zend, SM) ?
2. Когда нам все таки не обойтись от локатора и где его есть смысл использовать?
 

fixxxer

К.О.
Партнер клуба
Много букв и все не по делу.

DI - это когда так:

PHP:
class Foo {
   function __construct($dependency) {
       $this->dependency = $dependency;
   }
}

$dependency = new Dependency();
$object = new Foo($dependency);
SL - это когда так:

PHP:
class Foo {
   function __construct() {
       $this->dependency = new Dependency();
   }
}

$object = new Foo();
То, что на самом деле там где-то будет не new, а вызов чего-то там - не имеет особого значения.
 
Последнее редактирование:

esase

Новичок
Много букв и все не по делу.

DI - это когда так:

PHP:
class Foo {
   function __construct($dependency) {
       $this->dependency = $dependency;
   }
}

$dependency = new Dependency();
$object = new Foo($dependency);
SL - это когда так:

PHP:
class Foo {
   function __construct() {
       $this->dependency = new Dependency();
   }
}

$object = new Foo();
То, что на самом деле там где-то будет не new, а вызов чего-то там - не имеет особого значения.
Абсолютно не верное утверждение примера номер два.
По первому согласен Di (зависимые инъекции)

по второму:
То что вы написали называется - композиция (т..е вы создаете напрямую некий объект внутри другого объекта)

я же пишу об отличиях Dic (Ioc) контейнеров от Service Locator
и об
отличии Service Locator ot Di.
 

MiksIr

miksir@home:~$
Основной плюс DI - известные зависимости. Т.е. вы спокойно берете класс и смотрите - от чего он зависит. Т.е. знаете - какие моки подготовить и т.п.
Если же класс зависит от сервис локатора, вне зависимости - инжектируется он в класс и берется там через синглтон, вы не знаете зависимости класса - вам нужно будет пройтись по всему коду и найти - какие зависимости были вытянуты из сервис-локатора.
И вопрос соответствия теста и класса при изменении последнего в случае сервис-локатора более проблемный.
Почему используют? Ибо SL проще, всему свое место. Советую к чтению http://www.martinfowler.com/articles/injection.html
 

WMix

герр M:)ller
Партнер клуба
MiksIr, просто подставляешь сразу мок сервис локатора (сервис локатор с моками). тк. тест пишется параллельно, и тестирует только маленькие части, никаких хождений по классу не нужно
 

fixxxer

К.О.
Партнер клуба
То что вы написали называется - композиция (т..е вы создаете напрямую некий объект внутри другого объекта)
Я ж говорю - мысленно замени new на обращение к DI/SL, суть не изменится
 

MiksIr

miksir@home:~$
MiksIr, просто подставляешь сразу мок сервис локатора (сервис локатор с моками).
Мок сервис локатора? ;) Мокать нужно сервисы, а не сам SL ;) Так как SL содержит много больше сервисов, чем нужно для данного класса - нужно смотреть какие.
тк. тест пишется параллельно, и тестирует только маленькие части, никаких хождений по классу не нужно
Это ваше гипотетическое предположение ;) Да и не именно в тестах дело.
 

Тугай

Новичок
Service Locator:

DiC:

Вечная путаница между DI и DiC. :)

Most of the time, you don't need a Dependency Injection Container to benefit from Dependency Injection. (Fabien Potencier)
По картинкам видно, что DiC много на себя берет используя DI создается и ClassA и его сервисы.

Service Locator - вообще то и не должен заботится о том как создать ServiceA и ServiceB.
C Service Manager - это могут быть фабрики, с Registry - просто хардкод.

DiC != SL
Но часто можно отказаться от использования SL и заменить на DI (роль DiC выполняет само приложение, разные уровни).
 

Вурдалак

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

P.S. Вторая картинка неверная: в общем случае сначала создаются зависимости, а уж потом только ClassA.
 

esase

Новичок
Я ж говорю - мысленно замени new на обращение к DI/SL, суть не изменится
Суть изменится существенно, если вы сами напрямую через композицию создаете объекты внутри других объектов (ключевое слово - создаете), то в будущем если захотите изменить или поменять реализацию созданного объекта, вы будете вынуждены искать все места где таким образом вы делали это дело. + такой способ будет вам мешать тестить объекты (вы не сможете никак подменить объекты созданные на прямую).

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

Да хватит вам мусолить, все прекрасно знают определения. У автора другого рода проблемы, это уже fixxxer отмечал.

P.S. Вторая картинка неверная: в общем случае сначала создаются зависимости, а уж потом только ClassA.
Суть вопроса была - как мне удобнее, правильнее и какой способ выбрать когда у классов на основе одного интерфейса разные потребности. Одному нужно заинжектить модель, второму объект запроса, модуль и еще чего нить.

Варианты:
1. Передать туда SL - и пусть объект вытягивает сам, что ему нужно.
2. Сделать фабрику которая на основе Switch case смотрела что за класс требуется и инжектила туда необходимое ().
3. В фабрике юзать Di фреймворок который сам определит что нужно вашему классу и сам заинжектит туда все, что нужно.
 

AmdY

Пью пиво
Команда форума
esase, какая нафик разница, кроме религиозной? всегда нужно искать равновесие, допустим глобальные зависимости класса инджектить в конструктор, плюс туда же контейнер с сервисами, чтобы методы тянули нехватающие локальные, при этом не портя апи.
PHP:
namespace Acme\Controller;

class User {
    public function __construct(Ioc $ioc, UserRepository $user) {} //
    public function login() {
        $this->user->login(); // используем глобальный сервис
    }
    public function registration() {
        $this->user->create(); // глобальный
        $this->ioc->mailer->send(); // не загромождать же объект ненужными сервисами, нужным только в одном методе.
    }

}
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Одному нужно заинжектить модель, второму объект запроса
Да тебе говорят, что ты инжектишь какую-то херню, это в аргументы обычного метода тебе нужно передавать. В конструктор инжектят то, что есть на этапе конфигурации, либо через фабрику. Здесь мне трудно посоветовать что-то конкретное, потому что пример какой-то говёный: какому-то платёжному сервису нужен IRequest (щито?!), какому — какой-то говёный IModel (щито?!).

И что такое вообще «модель»? Модель чайника? Мобильника? «Модель» — это совокупность (или какой-то вполне конкретный класс — User, Book, etc.) сущностей, value object'ов и каких-то бизнес-сервисов, которые моделируют предметную область. Просто интерфейс IModel — это какое-то говно. Какие же там методы могут находится?

AmdY тоже какую-то херню несёт, в сущность User сервисы пихает, либо не умеет правильно именовать классы.
 
Сверху