На этом шаге мы рассмотрим небольшой пример.
В заключение рассмотрим небольшой пример, поясняющий некоторые вопросы, рассмотренные на предыдущих шагах.
#! perl -w package Staff; sub setdata { my ($self, $data) = @_; for $i (keys %$data) { $self->{$i}=$data->{$i}; } return $self; } sub new { my ($class, $data) = @_; my $self = $data; bless $self, $class; return $self; } sub showdata { my $self = shift; my @keys = @_ ? @_ : sort keys %$self; foreach $key (@keys) { print "\t$key => $self->{$key}\n"; } return $self; } sub AUTOLOAD { print "пакет Staff: отсутствует функция $AUTOLOAD\n"; } sub DESTROY { print "Удаляется объект класса Staff\n"; } #################################################################### package Graduate; @ISA = (Staff); sub new { my ($class, $data) = @_; # наследование переменной объекта my $self = Staff->new($data); $self->{"образование"}="высшее"; bless $self, $class; return $self; } sub showdata { my $self = shift; return $self if ($self->{"образование"} ne "высшее"); my @keys = sort keys %$self; foreach $key (@keys) { print "\t$key => $self->{$key}\n"; } return $self; } sub DESTROY { my $self= shift; $self->SUPER::DESTROY; print "Удаляется объект класса Graduate\n"; } #################################################################### package main; $someone=Graduate->new({"фамилия"=>"Кузнецов", "имя"=>"Николай"}); $somebody=Staff->new({"фамилия"=>"Петрова","имя"=>"Анна"}); $someone->showdata; $somebody->Graduate::showdata; $someone->getdata;
Для простоты все классы расположены в одном файле. Если класс занимает отдельный модуль, необходимо позаботиться об управлении экспортом имен при помощи списков @EXPORT и @EXPORT_OK, а также о подключении соответствующих модулей к вызывающей программе.
В данном примере определен пакет main и два класса: Staff и Graduate. Staff является базовым классом Graduate.
Наследование методов задается при помощи массива @ISA, ассоциированного с производным классом Graduate. Помимо наследования методов, которое обеспечивает Perl, организовано наследование переменных объекта через вызов в конструкторе класса Graduate конструктора базового класса Staff. В результате объект класса Graduate получает при создании переменную объекта Staff (параметр, переданный конструктору new), которую изменяет, добавляя в соответствующий хеш-массив новый элемент с ключом "образование" и значением "высшее".
Ситуация, когда конструктор производного класса вызывает конструктор базового, также обсуждалась на шаге 144. В подобном случае при удалении объекта автоматически вызывается только деструктор производного класса, хотя при вызове конструктора последовательно создавались объекты двух классов. В примере деструктор DESTROY класса Graduate вызывает деструктор базового класса. В данном случае это совершенно не обязательно, так как оба деструктора просто выводят сообщения об удалении своих объектов, но ведь это только модель. В иной ситуации такое решение может быть необходимым.
Следующая тема связана с поиском несуществующего метода, к которому происходит обращение внутри класса, и применением в этом случае функции AUTOLOAD. В пакете main вызывается метод getdata для объекта $someone. $someone является объектом класса Graduate, в котором метод getdata не определен. Обратите внимание на форму вызова метода getdata. Если бы он был вызван в виде getdata ($someone), это означало, что вызывается функция getdata из пакета main с параметром $someone. Поскольку в пакете main такая функция не определена, выполнение программы было бы завершено с выдачей сообщения вида:
Undefined subroutine &main::getdata called at ...
Форма обращения $someone->getdata однозначно определяет, что вызывается не функция, а метод объекта. Следовательно, его надо искать сначала в собственном классе Graduate, а затем в классе Staff, который определен в качестве базового для Graduate. В классе Staff метод getdata также не определен, но определена функция AUTOLOAD, которая и вызывается вместо этого метода.
Полиморфизм, т. е. переопределение методов базового объекта, показан на примере метода showdata. Класс Staff служит для порождения анкетных форм учета персонала. Его подкласс Graduate описывает подмножество таких форм только для лиц с высшим образованием. Конструктор класса Graduate автоматически добавляет в форму запись о наличии высшего образования. Метод showdata, наследованный у базового класса, изменен таким образом, чтобы игнорировать анкеты без такой записи. Поэтому информация об объекте $somebody, принадлежащем к базовому классу, напечатана не будет.
В результате выполнения примера будут выведены строки, соответствующие действиям, заданным в программе:
Рис.1. Результат работы программы
Со следующего шага мы начнем знакомиться с опциями интерпретатора perl.