HTML page
11.7. Применение замыканий вместо объектов
Проблема
Вы хотите работать с записями, обладающими определенным состоянием, поведением и идентичностью, но вам не хочется изучать для этого объектно-ориентированное программирование.Решение
Напишите функцию, которая возвращает (по ссылке) хэш ссылок на фрагменты кода. Все эти фрагменты представляют собой замыкания, созданные в общей области действия, поэтому при выполнении они будут совместно использовать одни и те же закрытые переменные.Комментарий
Поскольку замыкание представляет собой совокупность кода и данных, одна из реализации позволяет имитировать поведение объекта. Следующий пример создает и возвращает хэш анонимных функций. Функция mkcounter получает начальное значение счетчика и возвращает ссылку, позволяющую косвенно оперировать им.
$с1 = |
inkcounter(20); |
|||
$с2 = |
mkcounter(77); |
|||
printf |
"next |
c1: %d\n", |
$c1->{NEXT}->(); |
# 21 |
printf |
"next |
c2: %d\n", |
$c2->{NEXT}->(); |
# 78 |
printf |
"next |
c1: %d\n", |
$c1->{NEXT}->(); |
# 22 |
printf |
"last |
c1: %d\n", |
$c1->{PREV}->(); |
# |
printf |
"old |
c2: %d\n", |
$c2->{RESET}->(); |
# 77 |
sub mkcounter { |
|||||
my |
$count = |
shift; |
|||
my |
$start = |
$count; |
|||
my |
$bundle = |
{ |
|
|
|
|
"NEXT" |
=> |
sub { |
[ return |
++$count |
|
"PREV" |
=> |
sub { |
i return |
--$count |
|
"GET" |
=> |
sub { |
'. return |
$count |
|
"SET" |
=> |
sub { |
[ $count |
= shift |
|
"BUMP" |
=> |
sub \ |
; $count |
+= shift |
|
"RESET" |
=> |
sub { |
[ $count |
= $start |
$bundle->{"LAST"}=$bundle->{"PREV"};
return $bundle;
Поскольку лексические переменные, используемые замыканиями в ссылке на хэш $bundle, используются функцией, они не освобождаются. При следующем вызове mkcounter замыкания получают другой набор привязок переменных для того же кода. Никто не сможет обратиться к этим двум переменным за пределами замыканий, поэтому полная инкапсуляция гарантирована. В результате присваивания, расположенного непосредственно перед return, значения "prev" и "last" будут ссылаться на одно и то же замыкание. Если вы разбираетесь в объектно-ориентированном программировании, можете считать их двумя разными сообщениями, реализованными с применением одного метода. Возвращаемая нами совокупность не является полноценным объектом, поскольку не поддерживает наследования и полиморфизма (пока). Однако она несомненно обладает собственным состоянием, поведением и идентификацией, а таkже обеспечивает инкапсуляцию.
> Смотри также -------------------------------
Замыкания рассматриваются perlref(1). Также см. главу 13, рецепты 10.11; ll,4
11.8. Создание ссылок на методы
Проблема
Требуется сохранить ссылку на метод.Решение
Создайте замыкание, обеспечивающее вызов нужного метода для объекта.Комментарий
Ссылка на метод - это нечто большее, чем простой указатель на функцию. Вам также придется определить, для какого объекта вызывается метод, поскольку исходные данные для работы метода содержатся в объекте. Оптимальным решением будет использование замыкания. Если переменная $obj имеет лексическую область действия, воспользуйтесь следующим фрагментом:$mref = sub { $obj->meth(@_) };
# Позднее...
$mref->("args", "go", "here"); Даже когда переменная $obj выходит из области действия, она остается в замыкании, хранящемся в Smref. Позднее при косвенном вызове метода будет использован правильный объект. Учтите, что формулировка: $sref = \$obj->meth; работает не так, как можно предположить. Сначала она вызывает метод объекта, а затем дает ссылку либо на возвращаемое значение, либо на последнее из возвращаемых значений, если метод возвращает список. Метод can из базового класса UNIVERSAL выглядит заманчиво, но вряд ли делает именно то, что вы хотите:
$cref = $obj->can("meth");
Он дает ссылку на код соответствующего метода (если он будет найден), не несущую информации об объекте. В сущности, вы получаете обычный указатель на функцию. Информация об объекте теряется. Из-за этого и понадобилось замыкание, запоминающее как состояние объекта, так и вызываемый метод.
> Смотри также -------------------------------
Описание методов во введении к главе 13; рецепты 11.7; 13.7.