Думаю многие понимают значение баззвордов Inversion of Control (Ioc) и Dependency Injection (DI). Если не очень, но интересно — на хабре было несколько статей на эту тему, очень познaвательно и доступно изложено.
Методики отличные, но применить их в настоящей жизни как-то не получалось.
Под катом — небольшой обзор плачевного состояния дел в Perl и самостийное «кажется» решение.
Итак, почему не выходило применить что-то в реальном коде.
На самом деле все довольно понятно — имеющиеся модули, заявляющие реализацию DI — сложны для понимания и фантастически сложны в использовании, причем кода получается на меньше, а больше. И очень странного.
Пример из Bread::Board manual, который (мануал) раскинулся на 4-х страницах!
Пример IOC::Slinky::Container, уже лучше, но все та же жесть, если задуматься.
Пример Peco::Container — позиционируемый как «Light Inversion of Control (IoC) container». Попробуйте взять последнюю строку и проследить за происходящим в обратном порядке. Все еще понимаете, что происходит?
ИМХО проблема вся в том, что эти реализации сложны, перегружены какими-то фичами и в итоге невкуряемы, неудобны в применении и могут превратить поддержку в ад.
Все, что нужно от DIc — возможность положить туда кусок кода и позднее получить результат его выполнения. Положили заготовку dbh — получили рабочий хендлер на выходе. Никакой магии, создания кода из файла конфигурации и прочих академических штук.
Пример Kaiten::Container — просто, наглядно и эффективно.
Примеры и документация в комплекте, взять можно на CPAN и github.
Вопросы, благодарности, проклятия и лучи ненависти можно опубликовать здесь или любым другим удобным Вам образом.
PS. Если модуль не желает ставится, сообщая что-то типа
Присоединяйтесь к жаждущим патча Moo здесь.
PPS. А теперь и в 3D! Поддерживается разрешение зависимостей.
Методики отличные, но применить их в настоящей жизни как-то не получалось.
Под катом — небольшой обзор плачевного состояния дел в Perl и самостийное «кажется» решение.
Итак, почему не выходило применить что-то в реальном коде.
На самом деле все довольно понятно — имеющиеся модули, заявляющие реализацию DI — сложны для понимания и фантастически сложны в использовании, причем кода получается на меньше, а больше. И очень странного.
$c->add_service( Bread::Board::BlockInjection->new( name => 'authenticator', block => sub { my $service = shift; Authenticator->new( db_conn => $service->param('db_conn'), logger => $service->param('logger') ); }, dependencies => { db_conn => Bread::Board::Dependency->new( service_path => 'db_conn' ), logger => Bread::Board::Dependency->new( service_path => 'logger' ), } ) );
Пример из Bread::Board manual, который (мануал) раскинулся на 4-х страницах!
$c = IOC::Slinky::Container->new( config => { container => { # constructor injection dbh => { _class => "DBI", _constructor => "connect", _constructor_args => [ "DBD:SQLite:dbname=/tmp/my.db", "user", "pass", { RaiseError => 1 }, ], }, # setter injection y2k => { _singleton => 0, _class => "DateTime", year => 2000, month => 1, day => 1, }, } } );
Пример IOC::Slinky::Container, уже лучше, но все та же жесть, если задуматься.
my $c = Peco::Container->new; $c->register('log_mode', O_APPEND); $c->register('log_file', '/var/log/my-app.log'); $c->register('log_fh', 'IO::File', ['log_file', 'log_mode']); $c->register('my_logger', 'My::Logger', ['log_fh']);
Пример Peco::Container — позиционируемый как «Light Inversion of Control (IoC) container». Попробуйте взять последнюю строку и проследить за происходящим в обратном порядке. Все еще понимаете, что происходит?
ИМХО проблема вся в том, что эти реализации сложны, перегружены какими-то фичами и в итоге невкуряемы, неудобны в применении и могут превратить поддержку в ад.
Все, что нужно от DIc — возможность положить туда кусок кода и позднее получить результат его выполнения. Положили заготовку dbh — получили рабочий хендлер на выходе. Никакой магии, создания кода из файла конфигурации и прочих академических штук.
my $kaiten_config = { examplep_config => { handler => sub { { RaiseError => 1 } }, probe => sub { 1 }, settings => { reusable => 1 }, }, ExampleP => { handler => sub { my $c = shift; my $conf = $c->get_by_name('examplep_config'); return DBI->connect( "dbi:ExampleP:", "", "", $conf ) or die $DBI::errstr; }, probe => sub { shift->ping() }, settings => { reusable => 1 } } }; my $container = Kaiten::Container->new( init => $kaiten_config ); my $dbh = $container->get_by_name('ExampleP');
Пример Kaiten::Container — просто, наглядно и эффективно.
Примеры и документация в комплекте, взять можно на CPAN и github.
Вопросы, благодарности, проклятия и лучи ненависти можно опубликовать здесь или любым другим удобным Вам образом.
PS. Если модуль не желает ставится, сообщая что-то типа
# Failed test 'use Kaiten::Container;' # at t/00-load.t line 6. # Tried to use 'Kaiten::Container'. # Error: Argument "1.12_03" isn't numeric in numeric gt (>)...
Присоединяйтесь к жаждущим патча Moo здесь.
PPS. А теперь и в 3D! Поддерживается разрешение зависимостей.
