Отложенное создание DI зависимостей

Предварительное обсуждение найденных ошибок перед отправкой их авторам фреймворка, а также внесение новых предложений.
Ответить
Аватара пользователя
mitaichik
Сообщения: 512
Зарегистрирован: 2010.09.24, 21:18
Откуда: Россия, Санкт-Петербург

Отложенное создание DI зависимостей

Сообщение mitaichik »

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

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

Предлагаю рассмотреть ввод понятния отложенного создания зависимости.
То есть внедряется не сама зависимость, а, грубо говоря, ее геттер, который создает инстанс зависимости когда он вызывается, или берет уже созданный (если это синглтон). Такой объект будет крайне легковесным.

Наверное, документация Dagger чуше опишет что я имею виду http://google.github.io/dagger/users-gu ... injections (раздел Lazy injections)
Последний раз редактировалось mitaichik 2016.11.16, 00:32, всего редактировалось 1 раз.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Отложенное создание DI зависимостей

Сообщение ElisDN »

А какие проблемы с созданием нескольких сервисов сразу?
Аватара пользователя
mitaichik
Сообщения: 512
Зарегистрирован: 2010.09.24, 21:18
Откуда: Россия, Санкт-Петербург

Re: Отложенное создание DI зависимостей

Сообщение mitaichik »

ElisDN писал(а):А какие проблемы с созданием нескольких сервисов сразу?
У меня проблем нет, но это решение для тех, кто не хочет создавать неиспользуемые сервисы, например, ввиду их тяжелости создания (я имею ввиду долгой инициализации).
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Отложенное создание DI зависимостей

Сообщение ElisDN »

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

А так если сервисов много, то пусть либо дёргают Yii::createObject или Yii::$container->get напрямую в экшене (вместо инъекции в конструктор контроллера), либо разбивают контроллеры на более узкоспециализированные.

А ленивую загрузку можно сделать и самому:

Код: Выделить всё

$container->set('my\Service', function ($container) {
    return Lazy::create('my\Service', $container);
}); 
Где в фабрике на лету делаем прокси:

Код: Выделить всё

class Lazy
{
    public static function create($class, Container $container, $arguments = null)
    {
        $methods = (new ReflectionClass($class))->getMethods(ReflectionClass::IS_PUBLIC);
        $methodsCode = implode(PHP_EOL, array_map(function (Method $method) {
            return 'public function ' . $method->getName() . '( '. ... . ') { return $this->getOriginalObject()->' . $method->getName() . '(' . ... . '); }';
        }, array_filter($methods, function (Method $method) {
            return !$method->isStatic();
        })));
        $proxyClass = $class . 'Proxy' . $rand;
        eval("class $proxyClass extends $class {
            use ProxyTrait;
            $methodsCode
        }");
        return new $proxyClass($class, $container, $arguments);
    }
} 

Код: Выделить всё

trait ProxyTrait
{
    private $_class;
    private $_container;
    private $_arguments;
    private $_object;
    public function __construct($class, $container, $arguments)
    {
        $this->_class = $class;
        $this->_container = $container;
        $this->_arguments = $arguments;
    }
    protected getOriginalObject()
    {
        if ($this->_object === null) {
            $arguments = is_array($this->_arguments) ? $this->_arguments : call_user_func($this->_arguments, $this->_container);
            $this->_object = (new ReflectionClass($this->_class))->newInstanceArgs($arguments);
        }
        return $this->_object;
    }
} 
Ответить