Moose — расширения для Perl 5, позволяющее упростить реализацию ООП.
Создание класса происходит очень легко:
package MyClass;
use Moose;
все, пустой класс создан. Он может иметь произвольное количество: аттрибутов, методов, суперклассов, модификаторов метода, конструктор(1шт), деструктор(1шт) и мета-класс(1шт) который содержит все метаинформацию о данном классе.
Теперь подробнее об этих составляющих:
Класс
Каждый класс является подклассом Moose::Object. По умолчанию создает кучу всяких полезных объектов, в виде мета-классов и других присущих суперклассу объектов, к которым можно получить доступ после создания.
Подкласс
Наследование происходит с помощью ключевого слова extends
package User;
use Moose;
extends 'Person';
has 'username' => ( is => 'rw' );
множественное наследование, указываем классы через запятую: extends 'Foo', 'Bar';
Аттрибуты
Обязательно должно быть имя у аттрибута, может иметь различное количество свойств: флаг read/write, type, accessor method, delegations, значение по умолчанию и др.
По умолчанию Moose хранит аттрибуты в экземпляре класса, как хэш. Доступ к ним можно получить через аксессоры.
has — объявляем аттрибут
has 'first_name' => ( is => 'rw' );
опция is может иметь значения [ro|rw|bare] read-only либо read-write или bare — без аксессоров к данному аттрибуту.
Геттеры и сеттеры можно установить так:
has 'weight' => (
is => 'rw',
reader => 'get_weight',
writer => 'set_weight',
);
predicate — если аттрибут «undef» или другое ложное значение, то возвратит истину.
clearer — сбрасывает аттрибут
clearer — сбрасывает аттрибут
has 'ssn' => (
is => 'rw',
clearer => 'clear_ssn',
predicate => 'has_ssn',
);
$person->has_ssn; # false
$person->ssn(undef);
$person->ssn; # returns undef
$person->has_ssn; # true
$person->clear_ssn;
$person->ssn; # returns undef
$person->has_ssn; # false
$person->ssn('123-45-6789');
$person->ssn; # returns '123-45-6789'
$person->has_ssn; # true
Необходимость в установки аттрибута
можно установить с помощью свойства: required => 1
по умолчанию все аттрибуты необязательны.
Аттрибуты можно установить по умолчанию двумя способами.
has 'size' => (
is => 'ro',
default => 'medium',
predicate => 'has_size',
);
и если не устанавливать в конструкторе, то:
my $person = Person->new();
$person->size; # medium
$person->has_size; # true
А можно установить ссылку на метод:
has 'size' => (
is => 'ro',
default =>
sub { ( 'small', 'medium', 'large' )[ int( rand 3 ) ] },
predicate => 'has_size',
);
Альтернативный метод:
has 'size' => (
is => 'ro',
builder => '_build_size',
predicate => 'has_size',
);
sub _build_size {
return ( 'small', 'medium', 'large' )[ int( rand 3 ) ];
}
builder рекомендуют использовать вместо default.
Откладываем установку аттрибута в последнюю очередь
этого можно достичь установив свойство: lazy => 1
особенно необходимо, когда начальное значение аттрибута зависит от других факторов.
Еще один плюс в использовании этого свойства, в том что аттрибут будет вычисляться только в том случае, если потребуется, а если же он вообще не потребуется, то мы сохраним CPU time.
Типы аттрибутов
могут быть такой же тип как и любая перл структура: isa => 'Str'
а могут быть и объектом: does => 'MyApp::Object'
Установка нескольких аттрибутов
has [ 'x', 'y' ] => ( is => 'ro', isa => 'Int' );
или так:
for my $name ( qw( x y ) ) {
my $builder = '_build_' . $name;
has $name => ( is => 'ro', isa => 'Int', builder => $builder );
}
Методы
Всякая функция, объявленная в классе — это метод класса.
Модификаторы методов
вызываются перед(или «before, after, around, augment») вызовом метода:
before 'atribute' => sub {
my $self = shift;
my $a = shift;
print $a;
};
Роль
поведение или состояние, которое должен реализовать данный класс. Аналог интерфейса в других ООязыках.
Создается с помощью use Moose::Role;
package Eq;
use Moose::Role;
requires 'equal_to';
sub not_equal_to {
my ( $self, $other ) = @_;
not $self->equal_to($other);
}
хотя роль выглядит как класс, но это не класс, нельзя создать экземпляр роли. Слово requires говорит нам, что любой класс, использующий данную роль должен реализовать метод 'equal_to'.
package Comparable;
use Moose;
with 'Eq';
sub equal_to {
my ( $self, $other ) = @_;
$self->compare($other) == 0;
}
sub compare {
my ( $self, $other ) = @_;
$self->amount <=> $other->amount;
}
Конструктор/Деструктор
Их никогда не нужно явно переопределять, но если все же возникнета такая необходимость то можно использовать BUILD() и DEMOLISH() для конструктора и деструктора соотвественно.
Metaclass
Метакласс описывает класс. С Moose каждый класс дает «meta()», который возвращает Moose::Meta::Class объект. Он говорит о том что представляет из себя данный класс.
my $meta = User->meta();
for my $attribute ( $meta->get_all_attributes ) {
print $attribute->name(), "n";
if ( $attribute->has_type_constraint ) {
print " type: ", $attribute->type_constraint->name, "n";
}
}
for my $method ( $meta->get_all_methods ) {
print $method->name, "n";
}
Очистка мусора
происходит если установить в начале класса: use namespace::autoclean;
или в конце класса: no Moose;
Ускорение работы классов
Если класс в процессе работы не изменяется, то его работу можно ускорить следущим образом:
__PACKAGE__->meta->make_immutable;
Вот и все, этих знаний должно быть достаточно, чтобы успешно создавать ООП структуры на языке Perl, но для более глубокого пониманию, конечно же лучше углубиться в документацию и… да да в исходный код модулей (ну это уж для особых случаев, хотя в данном случае, любопытство никому не помешает).
Автор: edem