Perl - подпишись и учись!

  Все выпуски  

Perl - подпишись и учись! - ООП в Perl


    Приветствую всех!(А именно 9250 подписчиков)

 От себя: на днях наткнулся на пост об ООП в Perl в блоге Андрея Костенко. (источник: http://kostenko.name/2008/12/04/oo-perl/). Сорри, Андрей, не пользуюсь rss-reader'ами, поэтому и задежка.. Тем не менее - мнение, выраженное здесь - Это ИМХО автора. Могу сказать, что с некоторыми вещами не согласен, но мнение нужно уважать. Тем более, что кому-то, я уверен, данный пост будет интересен и полезен. В посте - оригинальный текст, прошу не дергаться, если где-то чего не так )

Также прошу не забывать о форуме - вопросы по этой статье и Perl в целом - можно задать там. Автор, я уверен, зная его самолюбие, ответит )) Форум о Perl: http://forum.perl.dp.ua

 

 

ООП в Perl

Мы все знаем прекрасно какое ООП в Perl (оно прикручено через задницу). Поэтому напишу немного букаф о том, как всё-таки научить Perl чему-то объектно-ориентированному.

Методы доступа.

Писать $self->{attribute} – это конечно хорошо.

Но при этом интерпретатор не проверяет правильность написания аттрибута. Т.е. если вы где-то напишете $self->{atribute}, то Perl создаст новое св-во atibute, а Вы будете долго трахаться в поисках этой ошибки.

Также обращаться напрямую к аттрибутам не стоит по той причине, что если вы потом захотите сделать проверку значений, то вам это не удастся. Для более удобной работы с аттрибутами создаются методы доступа (accessors). В Perl методы доступа создаются модулем Class::Accessor:

Создание методов доступа:

package MyClass;
use Class::Accessor;
__PACKAGE__->follow_best_pactice;
__PACKAGE__->mk_accessors( qw/value1 value2/); #чтение/запись
__PACKAGE__->mk_ro_accessors( qw/ro_data/); #можно только читать
__PACKAGE__->mk_wo_accessors( qw/wo_data/); #можно только записывать. Даже автор модуля незнает нахрена это нужно (:

Class::Accessor автоматически создаёт конструктор, который устанавливает аттрибутам начальные значения.
Использование:

package main;
use feature qw/say/;
my $obj = MyClass->new( {
value1 => 5,
value2 => 'test',
ro_data => 'you can't write me'
} );
$obj->set_value1(6);
$obj->set_ro_data( '
qwerty' ); # это выдаст ошибку
say $obj->get_value2;

Наследование

С наследованием от одного класса – всё просто: “use parent ModuleName”. Именно use parent. От use base хотят избавиться потому что он хреновый.
Но вот с множественным наследованием у perl-а нелады. Если вы всё-таки решили воспользоваться множественным наследованием (и перед этим сто раз подумали. Потому что множественное наследование не рекомендуют использовать), то Perl преподносит вам неожиданный и неприятный сюрприз:
возьмём вот такой код:

package A;
sub method {
return __PACKAGE__;
}
 
package B;
use parent -norequire, qw/A/;
 
package C;
use parent -norequire, qw/A/;
sub method {
__PACKAGE__ .shift->SUPER::method();
}
package D;
use parent -norequire, qw/B C/;
sub method {
__PACKAGE__ .shift->SUPER::method();
}
package main;
use feature qw/say/;
say D->method

если его запустить, то он выведет строку “DA”, вместо правильной “DCA”. Это происходит потому что путь наследования определяется по масиву @ISA. В каком порядке модули запушились, в таком и вызываются.
Чтобы множественное наследование работало как следует, в perl5.10 нужно написать

use mro

В Perl 5.8 для этого необходимо поставить модуль Class::C3 и написать

man Class::C3 #ибо нефиг старьём пользоваться

Исключения

Про них я напишу попозже. Потому что Грабли закроют.
UPD: в грабли не успел :-(

Изначально в perl есть работа с исключениями с помощью eval/die. Исключения эти только строковые и отличить их от системных нельзя. Если нужны нормальные исключения – лезем в CPAN и устанавливаем модуль Exception::Class. До джавовских исключений ему далеко, но базовые функции он выполняет.

use Exception::Class (
'Exception',
'EHandsFromAss' => (
isa => 'Exception'
)
);
eval {
if ( $string == "" ) {
EHandsFromAss->throw('Использовать "==" для сравнения строк некошерно.')
}
};
if ( $e = Exception::Class->caught('Exception') { #наследники класса Exception
warn $e->error;
} elsif ( $e == Exception::Class->caught ) { #все остальніе исключения
ref $e
? $e->rethrow
: die $e; #это обычные die
}

Private/Protected методы

Perl не умеет создавать совсем приватные методы. Но метод, который начинается c подчеркивания:

sub _private {
}

- является private или protected. Это просто соглашение по использованию. Никто не мешает какому-то кретину вызвать его из другого объекта. Но для адекватных людей – отличная замена приватным методам.

Оффтопик

И ещё то, о чём нельзя забывать:

  • Документирование кода с помощью POD
  • Написание unit-тестов (Test::More)
package MyClass;
 
=head1 NAME
 
MyClass
 
=head1 DESCRIPTION
 
описание того что метод делает
 
=head1 SYNOPSIS
 
пример использования
 
my $obj = MyClass->new();
...
 
=head1 METHODS
 
=head2 new()
 
описание этого метода
...
=cut

В избранное