Раньше писал с использованием Moose, а давеча решил попробовать написать скелет работоспособного класса на чистом Perl.
Получилось так:
use v5.14.1;
use utf8;
use strict;
package FRC::skel;
use Carp qw(croak);
use Data::Dumper;
our $AUTOLOAD;
sub new {
my ($slfClass, %settings)=@_;
my %privArea;
my %attrs=(
'name'=>undef,
);
sub attrUniAccessor {
my ($attrName,$attrValue)=@_;
say scalar($attrValue?'Set':'Get')." $attrName ".scalar($attrValue?'to value: '.$attrValue:'value');
return undef unless exists($attrs{$attrName});
my $hndlAttr=$attrs{$attrName};
unless (ref($hndlAttr) eq 'HASH') {
if ($attrValue) {
return $privArea{$attrName}=$attrValue
} else {
return $privArea{$attrName}
}
} else {
if ($attrValue) {
return exists($hndlAttr->{'set'})?$hndlAttr->{'set'}->($attrValue):$privArea{$attrName}=$attrValue
} else {
return exists($hndlAttr->{'get'})?$hndlAttr->{'get'}->():$privArea{$attrName}
}
}
}
my (%methods, %cacheMethods);
%methods=(
'checkMethodExists'=>sub {
my $method=shift;
return [0, $methods{$method}] if exists($methods{$method});
return [1, \&attrUniAccessor] if exists($attrs{$method});
undef
},
'getMeOut'=>sub {
say 'Get me out called!';
},
'DESTROY'=> sub {
%methods=();
%attrs=();
say 'Ohh... You killed me :(';
}
);
my $obj=bless sub {
my ($method,@pars)=@_;
$method=~s%^.*::(.+?)$%$1%;
printf "You called method %s with [%s]\n", $method, join(','=>@pars);
return $methods{'checkMethodExists'}->(($pars[0]=~m/(?:^.+::)?(.+)$/)[0]) if $method eq 'checkMethodExists';
croak "No such method: $method" unless my $realProc=$cacheMethods{$method} || do { $cacheMethods{$method}=$methods{'checkMethodExists'}->($method) };
$realProc->[1]->(($realProc->[0]?$method:()),@pars);
};
while (my ($a,$v)=each %settings) {
$obj->($a,$v);
}
return $obj;
}
sub AUTOLOAD {
my ($slf)=@_;
say "You attempted to do: $AUTOLOAD";
no strict 'refs';
my $method=$AUTOLOAD;
croak sprintf("No such method %s in package %s\n", $method, ref($slf))
unless $method=~m/::(?:checkMethodExists|DESTROY)$/ or $slf->('checkMethodExists',$method);
*{$method}=sub { my $slf=shift; $slf->($method,@_) };
$method->(@_);
}
1;
Вывод «приложения», использующего этот класс, выглядит вот так (для примера)
You called method name with [Eihta Hamminess]
Set name to value: Eihta Hamminess
You attempted to do: FRC::skel::getMeOut
You called method checkMethodExists with [FRC::skel::getMeOut]
You called method getMeOut with []
Get me out called!
You attempted to do: FRC::skel::name
You called method checkMethodExists with [FRC::skel::name]
You called method name with []
Get name value
Eihta Hamminess
You called method getMeOut with []
Get me out called!
You called method name with [Kulvargia Zidatlis]
Set name to value: Kulvargia Zidatlis
You called method name with []
Get name value
My new name is: Kulvargia Zidatlis
You attempted to do: FRC::skel::DESTROY
You called method DESTROY with []
Ohh... You killed me :(
Собственно, от использования замыкания в качестве «сырья» для производства объекта отказываться точно не буду, от полной изоляции всех данных объекта, недоступности их для произвольной модификации извне - тоже.
Хотелось бы замечаний от тех, кто уже реализовывал что-то отдалённо похожее, но сделал это лучше (я всё ещё весьма далёк от понимания ООП,как удобной методологии разработки, и пока написал в ООП-стиле весьма немного).
P.S. pastebin.com залочен на работе, так что прошу прощения за эту адскую скатерть прямо в посте.