Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
my $dbh = $container->get_by_name('ExampleP'); получим в dbh? Результат работы DBI->connect( "dbi:ExampleP:", "", "", { RaiseError => 1 } ) or die $DBI::errstr или непосредственно вызов этого выражения?$dbh = $container->get_by_name('ExampleP'); в $dbh будет результат выполнения DBI->connect(...), но в отличии от простого $dbh = DBI->connect(...) можем в конфиге, например, тестовом указывать и что-то другое? Или же каждый раз когда мы будем использовать dbh в других выражениях вместо него каждый раз будет вызываться DBI->connect(...) типа макроса или переменной-функции? Так может понятнее :) my $config = {
examplep_config => {
handler => sub { { RaiseError => 1 } },
probe => sub { 1 },
settings => { reusable => 1 },
},
examplep_dbd => {
handler => sub { "dbi:ExampleP:" },
probe => sub { 1 },
settings => { reusable => 1 },
},
ExampleP => {
handler => sub {
my $c = shift;
my $dbd = $c->get_by_name('examplep_dbd');
my $conf = $c->get_by_name('examplep_config');
return DBI->connect( $dbd, "", "", $conf ) or die $DBI::errstr;
},
probe => sub { shift->ping() },
settings => { reusable => 1 }
},
};
В случае с Dipendency Injection мы передаем все зависимости в класс снаружи
my $dbh = $container->get_by_name('ExampleP');
my $person = Person->new( dbh => $dbh );
#===================================
package LoggerEngine2;
#===================================
# ...
sub output2 {
my $self = shift;
my $message = shift;
say( ( $self->level ? 'DEBUG ON: ' : 'DEBUG OFF: ' ) . $message );
}
# ...
$logger->output( 'it is worked at - ' . $full_name );
my $dbh = DBI->new();надо написатьmy $dbh = shift;IoC контейнеры исповедуют другую идеологию. Классы реализуют простейший механизм внедрения зависимостей — через конструктор, реже через сеттер.
При этом контейнер самостоятельно (это важно), на основе конфига, прокидывает зависимости в ничего не подозревающие объекты, поэтому такой подход называется push.
$container->register('foo', class => 'Foo', inject => 'new', arguments => ['dbh_production']);
my $dbh = ServiceLocator::instance()->getDbh();
my $dbh = DBI->new();my $dbh = shift; my $self = shift;
my $dbh = $self->dbh;$container->register('foo', class => 'Foo', inject => 'new', arguments => ['dbh_production']);$container->{'foo'} = Foo::new($container->{'dbh_production'});С использованием абстрактного контейнера в вакууме...
container 'Database' => as {
service 'dsn' => "dbi:sqlite:dbname=my-app.db";
service 'username' => "user234";
service 'password' => "****";
service 'dbh' => (
block => sub {
my $s = shift;
DBI->connect(
$s->param('dsn'),
$s->param('username'),
$s->param('password'),
) || die "Could not connect";
},
dependencies => wire_names(qw[dsn username password])
);
};
service 'logger' => (
class => 'FileLogger',
dependencies => [
depends_on('log_file_name'),
]
);
$self->registry->{'logger'} = FileLogger->new( $self->registry->{'log_file_name'});
sub new
{
$self->{dbh} = DBI->new();
$self->{user_agent} = LWP::UserAgent->new();
}
UrlFetcher->new();
sub new
{
my $locator = ServiceLocator::instance();
$self->{dbh} = $locator->getDbh();
$self->{user_agent} = $locator->getUserAgent();
}sub new
{
$self->{dbh} =shift;
$self->{user_agent} = shift;
}UrlFetcher->new(
ServiceLocator::instance()->getDbh(),
ServiceLocator::instance()->getUserAgent()
);а на верхнем уровне (в контроллере, например) разрешают зависимость
Я предлагаю переместится в одну ветку — ниже
А откуда у вас в $self->dbh возьмется, собственно, экземпляр DBI?
$self->dbh
Вроде там мы уже подошли совсем близко к тому, что, как написал выше koorchik: Kaiten::Container — это обычный Service Locator :)
<beans>
<bean id="reader"
class="com.copier.consoleReader"/>
<bean id="writer"
class="com.copier.systemLogWriter"/>
<bean id="copier"
class="com.copier.copier">
<property name="source">
<ref bean="reader"/>
</property>
<property name="destination">
<ref bean="writer"/>
</property>
</bean>
</beans>
'reader' => { handler => sub ( consoleReader->new() )},
'writer' => { handler => sub ( systemLogWriter->new() )},
'copier' => { handler => sub ( my $c = shift; copier->new( 'source' => $c->get('reader'), 'destination' => $c->get('writer')) )}
sub createCopier
{
return copier->new('source' => systemLogWriter->new(),
'destination' => consoleReader->new());
}
sub getCopier
{
my $self = shift;
$self->{copier} ||= copier->new('source' => systemLogWriter->new(),
'destination' => consoleReader->new());
} my $config = {
ExampleP => {
handler => sub {
return DBI->connect( "dbi:ExampleP:", "", "", { RaiseError => 1 } )
or die $DBI::errstr;
},
probe => sub { return 1 },
settings => { reusable => 1 }
},
};sub getExampleP
{
my $self = shift;
return $self->{dbh} ||= DBI->connect( "dbi:ExampleP:", "", "", { RaiseError => 1 } )
or die $DBI::errstr;
}Peco::Container, как вы верно подметили, позволяет декларативно объявить зависимости и в дальнейшем, на основе конфига, автоматически их разрешает.
package ServiceLocator;
...
sub getDbh
{
my $self = shift;
$self->{dbh} ||= DBI->connect(...);
}
…
my $dbh = $service_locator->getDbh();
my $config = {
dbi => {
handler => sub {
return DBI->connect()
},
settings => { reusable => 1 }
}
}
...
my $dbh = $container->get_by_name('dbi');
Почему в Perl так редко используется IoC, DI и магическая пилюля Kaiten::Container