Архитектурные вопросы в приложении (лог, настройки)

Absinthe

жожо
Какую проблему вечно пытаются решить ПХПшники глобалами - звездный доступ. Наговнял архитектурно? Не знаешь как получить компонент в той заднице где ты оказался? Твой выход - глобал и его семейство регистр, синглтон, контейнер etc.
И... DI. Потому что так же позволяет получить в любой жопе, любой компонент.
Ты точно не путаешь паттерны DI и ServiceLocator?
В DI ты не можешь получить компонент из любого места. У тебя есть строгая иерархия компонентов.
Нельзя иметь доступ к контроллеру из роутера и к роутеру из контроллера одновременно при помощи DI.
 

Вурдалак

Продвинутый новичок
HraKK, видимо ты не умеешь приводить примеры и/или объяснять. Я тоже не понял суть твоего баттхёрта в этой теме.
 

HraKK

Мудак
Команда форума
видимо, просто времени не много.

Смотрите батхерт очень простой.

У нас есть Заказ. И кто-то решил добавить функционал по распечатыванию заказа на чеке.

Есть класс Order. Что делает эфимерный программист с айкью ниже среднего - добавляет в него паблик метод print();
PHP:
class Order
{
    public function print ()
    {
    }
}
Так что делаем дальше? Дальше нам надо напечатать, поэтому создадим класс отвечающий за это Printer с паблик методом print()
PHP:
class Printer
{
    public function print()
    {
    }
}
А вот дальше у него уже появляются проблемы, как же в экземпляре заказа получить экземпляр принтера? Ага! Сделаем глобал переменную и туда положим принтер!
PHP:
$_GLOBAL['Order_Printer'] = new Printer();

class Order
{
    public function print ()
    {
         $_GLOBAL['Order_Printer']->print($this);
    }
}
Все круто, задача решена, но недомерки с форума обсмеяли решение, сказали что глобалы юзают только тупые. А надо DI. Окей, добавим немного DI в код.
PHP:
class Order
{
    protected $printer;

    public function setPrinter(Printer $object)
    {
         $this->printer = $object;
    }

    public function print ()
    {
         $this->printer->print($this);
    }
}

$di = new Di();
$container = $di->create('order', '\Order');
$container->setDependency('setPrinter', new Printer);
$order = $container->instantiate();

$order->print();
Все круто! Нет глобала, Di помог мне, мой код крутой! И Di Никто напрямую не дергает.

Пример конечно утрирован, но такое встречается повсеместно. И надо отдавать себе отчет, что этот тот же глобал только в профиль.
 

stalxed

Новичок
HraKK,
чем последний пример плох?
Ошибка здесь?
PHP:
$di = new Di();

$printerContainer = $di->create('printer', '\Printer');

$container = $di->create('order', '\Order');
$container->setDependency('setPrinter', $printerContainer); // передаём не объект принтера, а информацию о его создание.

$order = $container->instantiate();
$order->print();
Кстати, а почему метод setDependency вторым аргументом позволяет принимать произвольный объект? В нормально реализованном DI это не должно работать... Т.е. я лично не понимаю, как DI может выстрелить в ногу. Разве, что сам DI передавать в классы.
 
Последнее редактирование:

stalxed

Новичок
HraKK, что-то я реально не пойму, что вы высмеивайте в последнем примере.
Двойную зависимость $this->printer->print($this);, что принтер знает о заказе(иначе как он будет его печатать), а заказ о принтере.
Или что?
 

AnrDaemon

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

stalxed

Новичок
Пинтеру должно быть пофиг, что печатать. Сам заказ должен знать, как себя отформатировать для печати.
Не факт, что должен, я бы форматирование заказа для печати поручил отдельному классу.
Как-то так:
PHP:
$formatter = new OrderPrintFormatter($order)
$printer->print($formatter->format());
Одно не могу понять, в чём проблема с DI то?!
 

keltanas

marty cats
AnrDaemon, тут ни принтер не должен знать, как печатать заказ, ни заказ не должен знать, как самому себя распечатать.
Представь, что принтер - это твиг.
 

AnrDaemon

Продвинутый новичок
Да, давайте сейчас будем вспоминать про форматы обмена данными.
Хорошая тема, кстати. Кто как разводит представления для браузера и для принтера?
Мне надо будет скоро печать к одному проекту прикручивать, так что это вопрос меня интересует.
 

fixxxer

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

Пример конечно утрирован, но такое встречается повсеместно. И надо отдавать себе отчет, что этот тот же глобал только в профиль.
А вот как раз таки тут уже дело не в способе управления зависимостями, а в нарушении SRP и лишних зависимостях, не важно каким образом они берутся.
 

Absinthe

жожо
Все круто, задача решена, но недомерки с форума обсмеяли решение, сказали что глобалы юзают только тупые. А надо DI. Окей, добавим немного DI в код.
Но разница есть.
В одном случае твой класс будет зависеть от $GLOBALS (а не $_GLOBAL) и того, что в нем лежит.
В другом случае он будет зависеть только от интерфейса принтера (который ты забыл). От DI и его контейнера он зависеть не будет. Это будет нормальный класс, который решает только свою задачу, а не пытается что-то где-то взять. Наличие или отсутствие DI никак не повлияет на его код.

Т.е. я лично не понимаю, как DI может выстрелить в ногу. Разве, что сам DI передавать в классы.
за это в некоторых местах бьют по пальцам, иначе к Yii::app() скатиться можно. А тут разница с $GLOBALS уже минимальна.

Мне надо будет скоро печать к одному проекту прикручивать, так что это вопрос меня интересует.
Если приближенно, то пользователь сам Ctrl + P в браузере нажмет.
Если нужно строго определенные данные на печать, то стоит воспользоваться специальным форматом файла, который для этого предназначен (PDF).
 

fixxxer

К.О.
Партнер клуба
В общем, негодный пример, мы тут щас будем обсуждать, что у заказа не должно быть метода print, а не в чем проблема с DI
 

HraKK

Мудак
Команда форума
а не пытается что-то где-то взять
То что ты перенес вставку звездных объектов за пределы класса не убирает зависимости как таковой.

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

fixxxer

К.О.
Партнер клуба
Ну в этом смысле достаточно правила "инжектить только в конструктор". В конструкторе сразу видно говнозависимости. Ну и code review никто не отменял
 

HraKK

Мудак
Команда форума
Оно видно тому кто понимает, а кто такое написал - ему не ясно. И правило не абсолютное, не всегда нужна именно конструктор-инъекция.
 

Absinthe

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

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