Объектно-ориентированное программирование на Perl

В Perl существуют две различные реализации объектно-ориентированного программирования:

  • Одна основана на анонимных хеш-таблицах, где суть каждого экземпляра объекта — это ссылка на анонимную хеш-таблицу. В этой анонимной хеш-таблице хранятся все атрибуты экземпляра.

  • Другая основана на массивах. При определении класса мы создаём массив для каждого атрибута экземпляра, а суть каждого экземпляра объекта — это ссылка на индекс строки в этих массивах. В этих массивах хранятся все атрибуты экземпляра.


Основные понятия объектно-ориентированного программирования

В объектно-ориентированном программировании есть много основных понятий. Здесь мы рассмотрим три: объекты, классы и методы.

  • ОбъектОбъект — это ссылка на элемент данных в классе.

  • КлассКласс — это пакет Perl, содержащий методы, предоставляемые для объекта.

  • МетодМетод — это подпрограмма Perl, первый параметр которой — имя класса.

Perl предоставляет функцию bless(), которая используется для создания объектов. Связывая ссылку с именем класса через bless и возвращая эту ссылку, вы создаёте объект.


Определение класса

Класс — это всего лишь простой пакет.

Вы можете использовать пакет как класс, а функции в пакете — как методы класса.

Пакеты Perl предоставляют независимые пространства имён, поэтому методы и имена переменных из разных пакетов не конфликтуют.

Файлы классов Perl имеют расширение .pm.

Далее мы создаём класс Person:

package Person;

Область действия кода класса простирается до последней строки файла скрипта или до следующего ключевого слова "package".


Создание и использование объектов

Чтобы создать экземпляр (объект) класса, нужно определить конструктор. В большинстве программ в качестве конструктора используется имя класса, но в Perl можно использовать любое имя.

Вы можете использовать различные переменные Perl в качестве объектов Perl. В большинстве случаев мы используем ссылки на массивы или хеши.

Далее мы создаём конструктор для класса Person, используя ссылку на хеш Perl.

При создании объекта нужно предоставить конструктор — подпрограмму, которая возвращает ссылку на объект.

Пример приведён ниже:

Пример

package Person; sub new { my $class = shift; my $self = { _firstName => shift, _lastName => shift, _ssn => shift, }; # Вывод информации о пользователе print "Имя:$self->{_firstName}\n"; print "Фамилия:$self->{_lastName}\n"; print "Номер:$self->{_ssn}\n"; bless $self, $class; return $self; }

Далее мы создаём объект:

$object = new Person( "小明", "王", 23234345);

Определение методов

Методы класса Perl — это просто подпрограммы Perl, то есть то, что обычно называют функциями-членами.

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

Perl не предоставляет приватные переменные, но мы можем управлять данными объекта через вспомогательные методы.

Далее мы определим метод для получения имени:

sub getFirstName {
    return $self->{_firstName};
}

Это также можно записать так:

sub setFirstName {
    my ( $self, $firstName ) = @_;
    $self->{_firstName} = $firstName if defined($firstName);
    return $self->{_firstName};
}

Далее мы изменим код в файле Person.pm следующим образом:

Пример

#!/usr/bin/perl package Person; sub new { my $class = shift; my $self = { _firstName => shift, _lastName => shift, _ssn => shift, }; # Вывод информации о пользователе print "Имя:$self->{_firstName}\n"; print "Фамилия:$self->{_lastName}\n"; print "Номер:$self->{_ssn}\n"; bless $self, $class; return $self; } sub setFirstName { my ( $self, $firstName ) = @_; $self->{_firstName} = $firstName if defined($firstName); return $self->{_firstName}; } sub getFirstName { my( $self ) = @_; return $self->{_firstName}; } 1;

Код скрипта employee.pl выглядит следующим образом:

Пример

#!/usr/bin/perl use Person; $object = new Person( "Xiaoming", "Wang", 23234345); # Получить имя $firstName = $object->getFirstName(); print "Имя до установки:$firstName\n"; # Использовать вспомогательную функцию для установки имени $object->setFirstName( "Xiaoqiang" ); # Получить имя с помощью вспомогательной функции $firstName = $object->getFirstName(); print "Имя после установки:$firstName\n";

После выполнения вышеприведённой программы вывод:

$ perl employee.pl
名字:小明
姓氏:王
编号:23234345
设置前姓名为 : 小明
设置后姓名为 : 小强

Наследование

В Perl методы класса наследуются через массив @ISA, который содержит имена других пакетов (классов). Наследование переменных должно быть задано явно.

Множественное наследование — это когда этот массив @ISA содержит несколько имён классов (пакетов).

Через @ISA можно наследовать только методы; данные наследовать нельзя.

Далее мы создаём класс Employee, который наследуется от класса Person.

Код файла Employee.pm выглядит следующим образом:

Пример

#!/usr/bin/perl package Employee; use Person; use strict; our @ISA = qw(Person); # Наследовать от Person

Теперь класс Employee содержит все методы и атрибуты класса Person. Мы вводим следующий код в файл main.pl и выполняем его:

Пример

#!/usr/bin/perl use Employee; $object = new Employee( "Xiaoming", "Wang", 23234345); # Получить имя $firstName = $object->getFirstName(); print "Имя до установки:$firstName\n"; # Использовать вспомогательную функцию для установки имени $object->setFirstName( "Xiaoqiang" ); # Получить имя с помощью вспомогательной функции $firstName = $object->getFirstName(); print "Имя после установки:$firstName\n";

После выполнения вышеприведённой программы вывод:

$ perl main.pl
名字:小明
姓氏:王
编号:23234345
设置前姓名为 : 小明
设置后姓名为 : 小强

Переопределение методов

В приведённом выше примере класс Employee наследуется от класса Person. Однако если методы класса Person не удовлетворяют требованиям, их необходимо переопределить.

Далее мы добавляем несколько новых методов в класс Employee и переопределяем методы класса Person:

Пример

#!/usr/bin/perl package Employee; use Person; use strict; our @ISA = qw(Person); # Наследовать от Person # Переопределить конструктор sub new { my ($class) = @_; # Вызов конструктора родительского класса my $self = $class->SUPER::new( $_[1], $_[2], $_[3] ); # Добавить больше атрибутов $self->{_id} = undef; $self->{_title} = undef; bless $self, $class; return $self; } # Переопределить метод sub getFirstName { my( $self ) = @_; # Это функция подкласса print "Это функция подкласса\n"; return $self->{_firstName}; } # Добавить метод sub setLastName{ my ( $self, $lastName ) = @_; $self->{_lastName} = $lastName if defined($lastName); return $self->{_lastName}; } sub getLastName { my( $self ) = @_; return $self->{_lastName}; } 1;

Мы вводим следующий код в файл main.pl и выполняем его:

Пример

#!/usr/bin/perl use Employee; $object = new Employee( "Xiaoming", "Wang", 23234345); # Получить имя, используя модифицированный конструктор $firstName = $object->getFirstName(); print "Имя до установки:$firstName\n"; # Использовать вспомогательную функцию для установки имени $object->setFirstName( "Xiaoqiang" ); # Получить имя с помощью вспомогательной функции $firstName = $object->getFirstName(); print "Имя после установки:$firstName\n";

После выполнения вышеприведённой программы вывод:

$ perl main.pl
名字:小明
姓氏:王
编号:23234345
这是子类函数
设置前姓名为 : 小明
这是子类函数
设置后姓名为 : 小强

Автозагрузка

Если запрошенный метод не может быть найден в текущем классе, всех базовых классах текущего класса или классе UNIVERSAL, Perl ищет метод с именем AUTOLOAD(). Если AUTOLOAD найден, он вызывается, а глобальной переменной $AUTOLOAD присваивается полностью квалифицированное имя отсутствующего метода.

Если и это не сработает, Perl выдаёт ошибку и завершается с ошибкой.

Если вы не хотите наследовать AUTOLOAD из базового класса, всё просто: достаточно одной строки:

sub AUTOLOAD;

Деструкторы и сборка мусора

Когда освобождается последняя ссылка на объект, объект автоматически уничтожается.

Если вы хотите что-то сделать во время уничтожения, вы можете определить в классе метод с именем "DESTROY". Он автоматически вызывается в подходящий момент и выполняет дополнительные действия по очистке, которые вы определите.

package MyClass;
...
sub DESTROY
{
    print "MyClass::DESTROY called\n";
}

Perl передаёт ссылку на объект в качестве единственного параметра в DESTROY. Обратите внимание, что эта ссылка доступна только для чтения, то есть вы не можете изменить её, обратившись к $_

Если вы повторно примените bless к ссылке на объект до возврата деструктора, Perl затем вызовет метод DESTROY того объекта, для которого вы повторно применили bless, после возврата деструктора. Это даёт вам возможность вызвать деструктор базового класса или другого указанного вами класса. Следует отметить, что DESTROY также можно вызвать вручную, но обычно в этом нет необходимости.

После освобождения текущего объекта другие объекты, содержащиеся в текущем объекте, автоматически освобождаются.


Пример объектно-ориентированного программирования на Perl

Мы можем лучше понять применение ООП в Perl на следующем примере:

Пример

#!/usr/bin/perl # Ниже приведена простая реализация класса package MyClass; sub new { print "MyClass::new called\n"; my $type = shift; # Имя пакета my $self = {}; # Ссылка на пустой хеш return bless $self, $type; } sub DESTROY { print "MyClass::DESTROY called\n"; } sub MyMethod { print "MyClass::MyMethod called!\n"; } # Реализация наследования package MySubClass; @ISA = qw( MyClass ); sub new { print "MySubClass::new вызван\n"; my $type = shift; # Имя пакета my $self = MyClass->new; # Ссылка на пустой хэш return bless $self, $type; } sub DESTROY { print "MySubClass::DESTROY вызван\n"; } sub MyMethod { my $self = shift; $self->SUPER::MyMethod(); print " MySubClass::MyMethod вызван!\n"; } # Основная программа вызывает вышеуказанные классы package main; print "Вызов метода MyClass\n"; $myObject = MyClass->new(); $myObject->MyMethod(); print "Вызов метода MySubClass\n"; $myObject2 = MySubClass->new(); $myObject2->MyMethod(); print "Создание объекта с областью видимости\n"; { my $myObject2 = MyClass->new(); } # Автоматический вызов деструктора print "Создать объект\n"; $myObject3 = MyClass->new(); undef $myObject3; print "Выполнение скрипта завершено...\n"; # Автоматическое выполнение деструктора

При выполнении вышеуказанной программы вывод будет:

调用 MyClass 方法
MyClass::new called
MyClass::MyMethod called!
调用 MySubClass 方法
MySubClass::new called
MyClass::new called
MyClass::MyMethod called!
   MySubClass::MyMethod called!
创建一个作用域对象
MyClass::new called
MyClass::DESTROY called
创建对象
MyClass::new called
MyClass::DESTROY called
脚本执行结束...
MyClass::DESTROY called
MySubClass::DESTROY called
Другие расширения