Ну, есть "утка", которую используют другие динамические языки. UNIVERSAL::can
выполняет это до такой степени, что другие языки склонны использовать его.
$unknown->doodle() if $unknown->can( 'doodle' );
Название "утка" набирается из идеи, что "если она ходит как утка и говорит как утка ... это утка". Тем не менее, мы не видим, идет ли он «как утка», мы просто видим, что он делает то, что кто-то называет «гулять», потому что это то, что мы называем, что делает утка. Например, объект Tree Walker не " walk " как утка!
Вы не хотели бы передавать неправильный объект определенным вещам.
$path_or_OS_I_dont_know_which->format( "c:", 'y' );
Итак, насколько хорошо работает типирование утки, зависит от ваших ожиданий с другой стороны. Плюс это имеет тенденцию становиться грязным, если контракт более сложен. Вы просто умножаете неопределенность - хотя сужение объектов за счет маловероятной вероятности, что два класса случайно будут иметь 23 метода с одинаковыми именами.
if ( $duck->can( 'talk' ) && $duck->can( 'walk' )) {
$duck->walk();
$duck->talk();
}
это нормально, но
if ( $cand->can( 'walk' ) && $cand->can( 'talk' ) && ... && $cand->can( 'gargle' )) {
...
}
становится немного глупо. Кроме того, некоторые из этих методов на самом деле могут быть необязательными , и поэтому вам придется перемежать их с помощью:
$cand->snicker() if $cand->can( 'snicker' );
(Конечно, узлы это немного очистят.)
И это может стать действительно сложным с промежуточными продуктами методов, которые вы должны передать другому методу, который также может быть там.
Совместимость
У меня есть легкий шаблон, который я называю «Совместимый». Фактически нет пакета с таким названием, но если класс реализует то, что делает Date
, он помещает его в @ISA
;
our @ISA = qw<... Date::Compatible ...>;
Таким образом, есть только два isa
, которые нужно вызвать - и я в любом случае инкапсулирую это в подпрограмму is_one
:
sub UNIVERSAL::is_one {
my ( $self, @args ) = @_;
foreach my $pkg ( @args ) {
return 1 if $self->isa( $pkg );
}
return 0;
}
sub UNIVERSAL::is_compatible_with {
my ( $self, $class ) = @_;
return $self->is_one( $class, "${class}::Compatible" );
}
По крайней мере, это дает контракт для выполнения и для запроса кода для проверки. Реализация все еще может решить, насколько важно выполнить определенный метод контракта, если он не требователен.