Moose: ООП в Perl

в 22:24, , рубрики: perl, ооп, Программирование, метки: , ,

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


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js