Выбрать главу

package Private::Person; # класс "Личность"

sub new { # прототипом может быть

my $invocant = shift; # класс или объект

my $class = ref($invocant) || $invocant;

my $self = { # значения атрибутов:

NAME => '', # имя и

HEIGHT => 0.0 # рост

};

my $closure = sub { # функция доступа к данным

my $field = shift; # по имени атрибута

$self->{$field} = shift if @_; # изменим и

return $self->{$field}; # вернем значение

}; # объектом будет

bless($closure, $class); # ссылка на функцию

}

# метод доступа к атрибуту name

sub name {

my $self = shift; # ссылка на объект-функцию

&{$self}("NAME", @_); # доступ к скрытому значению

}

# метод доступа к атрибуту height

sub height { # то же, что выше, но несколько короче:

&{ $_[0] }("HEIGHT", @_[1 .. $#_ ] )

}

1;

Методы доступа к свойствам объектов получают первым аргументом ссылку на функцию доступа к значениям атрибутов и вызывают ее, передавая ей имя поля и остальные свои аргументы. Приведем пример создания объектов нового класса и обращения к их методам. В вызывающей программе все выглядит так, как будто данные по-прежнему хранятся в анонимном массиве:

package main; # вызывающая программа

use Private::Person; # использовать этот класс

my $elf = Private::Person->new; # создать объект и

$elf->name("Леголас"); # задать значения

$elf->height(189); # его атрибутам

# получить доступ к значениям атрибутов объекта

print $elf->name, ' ', $elf->height, ' ';

print ref($elf), "\n"; # тип референта: 'Private::Person'

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

Обратите также внимание на то, что конструктор класса Private::Person определен так, что он может вызываться с использованием либо имени класса, либо ссылки на существующий объект. Это проверяется в следующей строке:

my $class = ref($invocant) || $invocant;

Если первым аргументом передана ссылка на объект, то определяется имя его класса, иначе считается, что передано имя класса. Поэтому в программе можно создавать однотипные объекты, обращаясь к методу new() существующего объекта. Например, так:

my $hobbit = Private::Person->new; # вызов с именем класса

$hobbit->name("Bilbo Baggins");

my $frodo = $hobbit->new; # вызов со ссылкой на объект

$frodo->name("Frodo Baggins");

В классе могут быть определены методы, не предназначенные для работы с конкретными объектами. Такие методы называются методами класса или статическими методами. Для обращения к ним, так же как для обращения к конструктору, используется имя класса, а не ссылка на объект. Часто эти методы обслуживают данные, общие для всех объектов класса (то есть объявленные глобально на уровне класса). Подобные данные называются атрибутами класса. В качестве примера опишем класс Magic::Ring, где метод класса count() будет использоваться для доступа к значению атрибута класса $Magic::Ring::count, в котором будет храниться количество созданных волшебных колец.

package Magic::Ring; # класс "Магическое Кольцо"

sub new { # конструктор

my ($class, $owner) = @_; # имя класса и значение атрибута

$Magic::Ring::count++; # сосчитать новое Кольцо

bless({owner => $owner}, $class); # "благословить" хэш

}

sub owner { # метод чтения и записи атрибута owner

my $self = shift; # извлечь ссылку на объект

$self->{owner} = shift if @_; # изменить значение атрибута

return $self->{owner}; # вернуть значение атрибута

}

$Magic::Ring::count = 0; # атрибут класса: число Колец

sub count { # метод класса

return $Magic::Ring::count;

}

1; # конец описания класса Magic::Ring

В программе, использующей класс Magic::Ring, создается набор объектов. При каждом обращении к конструктору увеличивается счетчик созданных магических колец $Magic::Ring::count.

package main;

use Magic::Ring; # использовать класс "Магическое Кольцо"