Pull to refresh

Почему в Perl так редко используется IoC, DI и магическая пилюля Kaiten::Container

Perl *
Думаю многие понимают значение баззвордов Inversion of Control (Ioc) и Dependency Injection (DI). Если не очень, но интересно — на хабре было несколько статей на эту тему, очень познaвательно и доступно изложено.
Методики отличные, но применить их в настоящей жизни как-то не получалось.

Под катом — небольшой обзор плачевного состояния дел в 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! Поддерживается разрешение зависимостей.
Tags:
Hubs:
Total votes 17: ↑15 and ↓2 +13
Views 1.7K
Comments 56
Comments Comments 56

Posts