суббота, декабря 15, 2007

ООП в perl. Class::Std vs Object::InsideOut

Речь пойдёт о perl без perl6 синтаксиса.

Большинство знает что классов как таковых в perl5 нет. Вместо этого издавна использовался blessed hash, т.е. объект класса создавался примерно так(этот код естественно в соответствующем классу package):
sub new {
my $self = {};
$self->{NAME} = undef;
$self->{VERSION} = undef;
$self->{OPTIONS} = [];
bless($self);
return $self;
}

sub method1 {
...
}

sub _method2 {
...
}
Ну а далее вся работа с модулем строилась на условных соглашениях, таких как например имена приватных методов и полей начинаются с подчёркивания (см. выше _method2). Естественно были решения позволяющие сделать правильные приватные методы (приведу достаточно экзотический пример: http://perlmonks.org/?node_id=332744 ), но их использование для каждого класса было не очень удобным, так как заставляло делать много лишних телодвижений. Некоторые использовали соглашение о приватных методах (имена с подчёркиванием) и скрипт-сканер который проверял все файлы проекта на то что никто не вызвал приватный метод как публичный. Но на мой взгляд это всё костыли, хоть и работающие.

Многие наверное читали книгу Конвэя: Perl Best Practicies (далее PBP). Там он предлагает отказаться от использования blessed hash объектов и использовать InsideOut объекты. Не знаю как точно перевести это, ну можно например как объекты наизнанку :). Смысл в том что объект не является хэшом с полями а наоборот для каждого поля класса создаётся хэш или массив. В книге Конвэй в основном рассматривает свой собственный класс для использования InsideOut объектов: Class::Std . Но на практике он не всегда идеален. Рассмотрим например класс в котором в конструкторе кидается exception:

package cstd;

use Carp qw(croak);

use Class::Std;
{

sub sub1 : PRIVATE {
croak "sub1 exception";
}

sub BUILD {
sub1();
}
}
1;


Code syntax highlighting by VIM captured with ScreenShot script

И вот этот в котором exception в конструкторе не кидается:

package cstd2;

use Carp qw(croak);

use Class::Std;
{

sub sub1 {
croak "sub1 exception";
}

sub BUILD {

}
}
1;


Code syntax highlighting by VIM captured with ScreenShot script
И выполним вот этот тестовый скрипт:
#!/usr/bin/perl

use strict;
use warnings;
use cstd;
use cstd2;

my $a;

print "cstd:\n";
eval {
$a = cstd->new();
};

print $@ if ($@);

print "\ncstd2:\n";
$a = cstd2->new();
eval {
$a->sub1;
};

print $@ if ($@);


Code syntax highlighting by VIM captured with ScreenShot script

В результате получим вот это:
cstd:
sub1 exception at /usr/lib/perl5/site_perl/5.8.8/Class/Std.pm line 438

cstd2:
sub1 exception at cstd.pl line 20
Как видим в первом случае невоможно понять где произошло исключение, в то время когда exception генерится не в конструкторе - то получаем сообщение о том что exception в нашем скрипте на строке 20.

Создадим класс с exception в методе вызываемом в конструкторе с помощью Object::InsideOut :

package objInOut;
{
use strict;
use warnings;
use Carp qw(croak);
use Object::InsideOut;

sub sub1 :Private {
croak "sub1 exception";
}

sub _init :Init {
sub1();
}
}
1;


Code syntax highlighting by VIM captured with ScreenShot script


Выполним следующий скрипт:

#!/usr/bin/perl

use strict;
use warnings;
use objInOut;
use Carp qw(croak);

my $a;

print "object inside out:\n";
eval {
$a = objInOut->new();
};

print $@ if ($@);


Code syntax highlighting by VIM captured with ScreenShot script

В выводе получим:
object inside out:
OIO error: Trapped uncaught error
Error: sub1 exception at objinsout.pl line 12
Package: Carp
File: /usr/lib/perl5/5.8.8/Carp.pm
Line: 269


Trace begun at /usr/lib/perl5/5.8.8/Carp.pm line 269
Carp::croak('sub1 exception') called at objInOut.pm line 9
objInOut::sub1 at objInOut.pm line 13
Object::InsideOut::new('objInOut', 'HASH(0x847d020)') called at objinsout.pl line 12
eval {...} at objinsout.pl line 11

Тут как видим уже легко понять где точно произошло исключение.

Кроме этого Object::InsideOut поддерживает threads - т.е. я бы советовал использовать именно эту реализацию InsideOut классов. Вобщем это ещё одно подтверждения того что часто бывает что удачная реализация удаётся не идейным вдохновителям а кому-то ещё...

Комментариев нет: