Perl Language
Объектно-ориентированный Perl
Поиск…
Создание объектов
В отличие от многих других языков, Perl не имеет конструкторов, которые выделяют память для ваших объектов. Вместо этого следует написать метод класса, который создает структуру данных и заполняет их данными (вы можете знать это как шаблон проектирования Factory Method).
package Point;
use strict;
sub new {
my ($class, $x, $y) = @_;
my $self = { x => $x, y => $y }; # store object data in a hash
bless $self, $class; # bind the hash to the class
return $self;
}
Этот метод можно использовать следующим образом:
my $point = Point->new(1, 2.5);
Всякий раз, когда оператор стрелки ->
используется с методами, его левый операнд добавляется к данному списку аргументов. Итак, @_
в new
будет содержать значения ('Point', 1, 2.5)
.
Там нет ничего особенного во имя new
. Вы можете вызывать заводские методы, как вы предпочитаете.
В хешах нет ничего особенного. Вы можете сделать то же самое следующим образом:
package Point;
use strict;
sub new {
my ($class, @coord) = @_;
my $self = \@coord;
bless $self, $class;
return $self;
}
В общем, любая ссылка может быть объектом, даже скалярной ссылкой. Но чаще всего хеши являются наиболее удобным способом представления данных объекта.
Определение классов
В общем, классы в Perl - это просто пакеты. Они могут содержать данные и методы, как обычные пакеты.
package Point;
use strict;
my $CANVAS_SIZE = [1000, 1000];
sub new {
...
}
sub polar_coordinates {
...
}
1;
Важно отметить, что переменные, объявленные в пакете, являются переменными класса, а не переменными объекта (экземпляра). Изменение переменной уровня пакета влияет на все объекты класса. Как хранить данные, специфичные для объекта, см. В разделе «Создание объектов».
Что делает пакеты классов конкретными, это оператор стрелки ->
. Он может использоваться после простого слова:
Point->new(...);
или после скалярной переменной (обычно содержащей ссылку):
my @polar = $point->polar_coordinates;
То, что слева от стрелки, добавляется к данному списку аргументов метода. Например, после вызова
Point->new(1, 2);
array @_
в new
будет содержать три аргумента: ('Point', 1, 2)
.
Пакеты, представляющие классы, должны учитывать это соглашение и ожидать, что все их методы будут иметь один дополнительный аргумент.
Разрешение наследования и методы
Чтобы сделать класс подклассом другого класса, используйте parent
прагму:
package Point;
use strict;
...
1;
package Point2D;
use strict;
use parent qw(Point);
...
1;
package Point3D;
use strict;
use parent qw(Point);
...
1;
Perl допускает множественное наследование:
package Point2D;
use strict;
use parent qw(Point PlanarObject);
...
1;
Наследование - это все о разрешении, метод которого следует вызывать в конкретной ситуации. Поскольку чистый Perl не устанавливает никаких правил о структуре данных, используемых для хранения данных объекта, наследование не имеет к этому никакого отношения.
Рассмотрим следующую иерархию классов:
package GeometryObject;
use strict;
sub transpose { ...}
1;
package Point;
use strict;
use parent qw(GeometryObject);
sub new { ... };
1;
package PlanarObject;
use strict;
use parent qw(GeometryObject);
sub transpose { ... }
1;
package Point2D;
use strict;
use parent qw(Point PlanarObject);
sub new { ... }
sub polar_coordinates { ... }
1;
Разрешение метода работает следующим образом:
Начальная точка определяется левым операндом оператора стрелки.
Если это голое слово:
Point2D->new(...);
... или скалярная переменная, содержащая строку:
my $class = 'Point2D'; $class->new(...);
... тогда отправной точкой является пакет с соответствующим именем (
Point2D
в обоих примерах).Если левый операнд является скалярной переменной, содержащей блаженную ссылку:
my $point = {...}; bless $point, 'Point2D'; # typically, it is encapsulated into class methods my @coord = $point->polar_coordinates;
то отправной точкой является класс ссылки (опять же,
Point2D
). Оператор стрелки не может использоваться для вызова методов для беспроблемных ссылок.
Если начальная точка содержит требуемый метод, она просто называется.
Таким образом, поскольку
Point2D::new
существует,Point2D->new(...);
просто назовут его.
Если начальная точка не содержит требуемого метода, выполняется поиск по глубине в
parent
классах. В приведенном выше примере порядок поиска будет выглядеть следующим образом:-
Point2D
-
Point
(первый родительPoint2D
) -
GeometryObject
(родительский элементPoint
) -
PlanarObject
(второй родительPoint2D
)
Например, в следующем коде:
my $point = Point2D->new(...); $point->transpose(...);
метод, который будет называться, это
GeometryObject::transpose
, хотя он будет переопределен вPlanarObject::transpose
.-
Вы можете установить начальную точку явно.
В предыдущем примере вы можете явно вызвать
PlanarObject::transpose
следующим образом:my $point = Point2D->new(...); $point->PlanarObject::transpose(...);
Аналогичным образом
SUPER::
выполняет поиск метода в родительских классах текущего класса.Например,
package Point2D; use strict; use parent qw(Point PlanarObject); sub new { (my $class, $x, $y) = @_; my $self = $class->SUPER::new; ... } 1;
вызовет
Point::new
в ходе выполненияPoint2D::new
.
Методы класса и объекта
В Perl разница между методами class (static) и object (instance) не так сильна, как на некоторых других языках, но она все еще существует.
Левый операнд оператора стрелки ->
становится первым аргументом вызываемого метода. Это может быть строка:
# the first argument of new is string 'Point' in both cases
Point->new(...);
my $class = 'Point';
$class->new(...);
или ссылку на объект:
# reference contained in $point is the first argument of polar_coordinates
my $point = Point->new(...);
my @coord = $point->polar_coordinates;
Методы класса - это только те, которые ожидают, что их первый аргумент будет строкой, а объектные методы - это те, которые ожидают, что их первый аргумент будет ссылкой на объект.
Обычно методы класса не делают ничего с их первым аргументом, который является просто именем класса. Как правило, он используется только самим Perl для разрешения метода. Поэтому типичный метод класса можно вызвать и для объекта:
my $width = Point->canvas_width;
my $point = Point->new(...);
my $width = $point->canvas_width;
Хотя этот синтаксис разрешен, он часто вводит в заблуждение, поэтому лучше его избегать.
Методы объектов получают ссылку на объект в качестве первого аргумента, поэтому они могут обращаться к данным объекта (в отличие от методов класса):
package Point;
use strict;
sub polar_coordinates {
my ($point) = @_;
my $x = $point->{x};
my $y = $point->{y};
return (sqrt($x * $x + $y * $y), atan2($y, $x));
}
1;
Тот же метод может отслеживать оба случая: когда он вызывается как класс или метод объекта:
sub universal_method {
my $self = shift;
if (ref $self) {
# object logic
...
}
else {
# class logic
...
}
}
Определение классов в современном Perl
Хотя доступный, определение класса с нуля не рекомендуется в современном Perl. Используйте одну из вспомогательных OO-систем, которые предоставляют больше возможностей и удобства. Среди этих систем:
Moose
- вдохновленный дизайном Perl 6 OOClass::Accessor
- легкая альтернатива MooseClass::Tiny
- действительно минимальный класс строитель
лось
package Foo;
use Moose;
has bar => (is => 'ro'); # a read-only property
has baz => (is => 'rw', isa => 'Bool'); # a read-write boolean property
sub qux {
my $self = shift;
my $barIsBaz = $self->bar eq 'baz'; # property getter
$self->baz($barIsBaz); # property setter
}
Класс :: Accessor (синтаксис Moose)
package Foo;
use Class::Accessor 'antlers';
has bar => (is => 'ro'); # a read-only property
has baz => (is => 'rw', isa => 'Bool'); # a read-write property (only 'is' supported, the type is ignored)
Класс :: Accessor (собственный синтаксис)
package Foo;
use base qw(Class::Accessor);
Foo->mk_accessors(qw(bar baz)); # some read-write properties
Foo->mk_accessors(qw(qux)); # a read-only property
Класс :: Крошечный
package Foo;
use Class::Tiny qw(bar baz); # just props
Роли
Роль в Perl в основном
- набор методов и атрибутов, которые
- вводится в класс напрямую.
Роль обеспечивает часть функциональности , которая может состоять в (или приложенной к) любой класс (который , как говорят потреблять роль). Роль не может быть унаследована, но может быть использована другой ролью.
Роль может также потребовать, чтобы классы потребления реализовали некоторые методы вместо реализации самих методов (как и интерфейсы на Java или C #).
Perl не имеет встроенной поддержки ролей, но есть классы CPAN, которые предоставляют такую поддержку.
package Chatty;
use Moose::Role;
requires 'introduce'; # a method consuming classes must implement
sub greet { # a method already implemented in the role
print "Hi!\n";
}
package Parrot;
use Moose;
with 'Chatty';
sub introduce {
print "I'm Buddy.\n";
}
Используйте, если ваша система OO не обеспечивает поддержку ролей (например, Class::Accessor
или Class::Tiny
). Не поддерживает атрибуты.
package Chatty;
use Role::Tiny;
requires 'introduce'; # a method consuming classes must implement
sub greet { # a method already implemented in the role
print "Hi!\n";
}
package Parrot;
use Class::Tiny;
use Role::Tiny::With;
with 'Chatty';
sub introduce {
print "I'm Buddy.\n";
}