LINUX.ORG.RU

Возможна ли подобная инкапсуляция в Perl?

 ,


1

2

Очень хочется делать так:

Описание класса:

package Object;
use strict;
use warnings;
our @EXPORT_OK=qw(new set_a get_a);
my $a='Hello';

sub new {
 bless {},shift
};

calc_a {
 my $v=shift;
 $v.=' here'
}

sub set_a {
 my $slf=shift;
 $a=calc_a(shift)
};

sub get_a {
 return $a
};

1;

Использование класса:

package main;
use strict;
use warnings;
use Object;

my $inst1=Object->new;
my $inst2=Object->new;

$inst1->set_a('Hot dog');
$inst2->set_a('Hot girl');

print join(' ','1)',$inst1->get_a,'2)',$inst2->get_a)."\n";

# $inst1->calc_a('Crazy man');

В результате должно быть выведено:

1) Hot dog 2) Hot girl

А если я раскомментирую последнюю строчку в main - Perl мне должен сказать «No such symbol calc_a in package Object» или что-то вроде этого - я же эту процедуру не экспортировал, а значит это «приватный метод».

Правильно ли я понимаю, что в Perl нельзя сделать так, чтобы переменные уровня пакета, в котором описан класс, были всё-таки переменными инстанса класса (конкретного объекта), а не просто переменными пакета?

Хранить данные в виде чего-то вроде $Config{refaddr $self}{'verbose'} - это по-моему извращение ещё более дикое, нежели хранение вообще всех данных внутри этой несчастной «благословлённой» (blessed) ссылки...

То есть чего бы хотелось:

1) чтобы при создании нового объекта происходило примерно то же, что и при fork'е процесса: все переменные пакета с их значениями копировались бы в адресное пространство объекта либо сразу, либо при первой же записи в них (а то копировать прямо всё и сразу может быть накладно).

2) нужно обязательное разделение методов на «приватные» и «публичные», чтобы я мог внутри объекта использовать те или иные процедуры, разрешая пользоваться извне лишь небольшим их подмножеством.

Можно ли как-то вот этак сделать в Perl или категорически нельзя?

P.S. Кстати, хранение всех собственных данных объекта в каком-нибудь громадном хэше, доступном по ссылке $ObjectData{refaddr $self}, то есть что-то вроде $ObjectData{refaddr $self}{'options'}{'user-defined'}{'var-a'} - это разве эффективно хотя бы с точки зрения скорости работы приложения? Как бы ни были эффективны хэши в Perl, по-моему все эти многоуровневые индексы куда менее эффективны, нежели простые переменные.

★★★★★

Последнее исправление: DRVTiny (всего исправлений: 2)
Ответ на: комментарий от gh0stwizard

Думаю, тебе нужно это.

Вау!

Да, это именно ОНО и есть!

Единственный наверное недостаток такого подхода - объекты будут создаваться долго и занимать больше памяти, но с другой стороны для таких вещей как сетевое взаимодействие в любом виде это не должно быть принципиально...

В конце-концов если уж совсем припрёт, можно что-то переписать в «функциональном» стиле, когда в процедуру передаются ссылки на все данные, которые ей нужны для работы.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Да, это именно ОНО и есть!
WARNING: Lexical subroutines are still experimental. The feature may be modified or removed in future versions of Perl.

Выдумал проблему и героически её решаешь.

anonymous
()
Ответ на: комментарий от gh0stwizard
drvtiny@drvtiny-ot-pc00:~/Apps/Perl5/examples$ ./use_cloj_class.pl 
Feature "lexical_subs" is not supported by Perl 5.14.2 at libs/OO/Test/Clojure.pm line 4.

Досадно :(

Такая простая и логичная вещь, а работает только начиная с 5.18, похоже. О чём же они раньше думали? :(

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Угу, я ещё given использую, а это якобы тоже такая фича, которая куда-то может быть removed :)

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Welcome to Perl's World!

#!/usr/bin/perl
# your code goes here

$\ = "\n";

sub fnc {
	print "fnc";
}

&fnc();

*main::fnc = sub {
	print "modified";
};

&fnc();

http://ideone.com/yKt3Om

Перл-хакинг (и бэд-стайл), не благодари.

P.S. Не будет работать в мультипотоках, а также при всяких таймерах, событийках и т.п., т.к. постоянно надо будет делать переопределение функции.

P.S.II. Используй анонимные функции $cb = sub {} они дешевле, чем изврат, что выше я написал.

gh0stwizard ★★★★★
()
Последнее исправление: gh0stwizard (всего исправлений: 3)
Ответ на: Welcome to Perl's World! от gh0stwizard

P.S.II. Используй анонимные функции $cb = sub {} они дешевле, чем изврат, что выше я написал.

Да, я тоже к этому пришёл в итоге. Синтаксис не ахти какой красивый (хотя если есть префикс типа do или get, то в общем читабельность кода не страдает) - зато работает!

Сейчас перепишу свой тестовый класс - выложу на обозрение широкой общественности :)

DRVTiny ★★★★★
() автор топика
Ответ на: Welcome to Perl's World! от gh0stwizard

Хотя... до меня только что дошло, какой ущерб есть от анонимных функций: нужно их обязательно полностью описывать до того, как они будут использованы первый раз (см $dbg_ и $setErr):

my $LCO=sub { die 'You must use "new" constructor' };

sub new {
 my ($class,%Config)=@_;
 my %Err;

 my $dbg_=sub {
  print STDERR @_,"\n" if $Config{'debug'};
 };

 my $doSetErr=sub {
  my ($c,$m)=@_;
  $dbg_->('Error[',$c,']: ',$m);
  @Err{'code','msg'}=($c,$m);
 };

 my %methods=(

  'debug'=>sub :lvalue {
    my $v=lc(shift);
    return $Config{'debug'} unless @_;
    if ($v=~m/^(?:[01]|true|false|yes|no)$/) {
     $Config{'debug'}=$v
    } else {
     $doSetErr->(127,'Wrong value for "debug" property')
    }
  },

 );

 $LCO=bless sub {
  my $method=shift;
  die 'No such method: '.$method unless $methods{$method};
  $methods{$method}->(@_);
 },$class


}

Здесь я не могу описать dbg_ и setErr где-нибудь в конце new, даже если объявлю эти переменные раньше!

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 3)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.