Думаю многие понимают значение баззвордов 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! Поддерживается разрешение зависимостей.