Создание классов и объектов
Класс — это пакет с набором методов .
Метод — это подпрограмма, встроенная в класс или объект. Метод использует ссылку на объект или имя класса,
передаваемое ему в качестве первого аргумента.
Объект — это ссылка на элемент данных. В отличие от других ссылок, этот элемент знает,
к какому классу он относится.
Рассмотрим пример класса Class1, для которого определен конструктор new.
В этом конструкторе создается ссылка на анонимный хеш, который будет хранить данные объекта.
package Class1;
sub new
{
my $self = {};
bless($self);
return $self;
}
return 1;
Для создания объекта вызывается конструктор, который обычно имеет имя new.
В следующем примере мы создаем объект определенного раннее класса Class1:
use Class1;
my $object = Class1->new();
Есть два типа методов: методы класса и методы объекта (методы экземпляров класса).
Метод класса вызывается с помощью имени класса, а метод экземпляра класса — с помощью объекта (ссылки).
package Class1;
sub new
{
my $self = {};
bless($self);
return $self;
}
sub name
{
my $class = shift;
print "This is the class method.\n";
return $class;
}
return 1;
use Class1
$classname = Class1->name();
$classname = Class1::name("Class1");
В следующем примере создается метод объекта data. Первый аргумент, передаваемый методу — это ссылка на сам объект.
Мы используем ее, чтобы сохранить данные в анонимном хеше
package Class1;
sub new
{
my $self = {};
bless($self);
return $self;
}
sub data
{
my $self = shift;
if (@_) {$self->{DATA} = shift;}
return $self->{DATA};
}
return 1;
use Class1
my $object1 = Class1->new();
$object1->data("Hello!");
print "Here is the text in the object: ",
$object1->data();
Here is the text in the object: Hello!
Хотя многие объектно-ориентированные языки программирования поддерживают приватные методы и переменные,
(то есть, внутренние методы и переменные, недостижимые вне класса или объекта),
Perl не позволяет делать этого явно.
В следующем примере, мы отслеживаем полное число объектов, созданных для конкретного класса,
запоминая его в переменной класса $total
package Cdata;
my $total = 0;
sub new
{
$self = {};
$total++;
return bless $self;
}
sub gettotal {return $total;}
return 1;
use Cdata;
$object1 = Cdata->new;
print "Current number of objects: ",$object1->gettotal, "\n";
$object2 = Cdata->new;
print "Current number of objects: ", $object2->gettotal, "\n";
$object3 = Cdata->new;
print "Current number of objects: ", $object3->gettotal, "\n";
Current number of objects: 1
Current number of objects: 2
Current number of objects: 3
Для выполнения команд Perl, связанных с уничтожением объектов, можно использовать деструкторы, — точнее,
они начинают работать, когда объект готов к уничтожению. В Perl работает автоматическая система сборки мусора.
Она уничтожает элементы и освобождает занимаемую ими память, когда они больше не нужны, — например,
когда потеряна ссылка на данные, переменная выходит за границы области видимости, либо интерпретатор прекращает работу.
В отличие от конструкторов, для деструкторов Perl имеется вполне определенное имя DESTROY:
package Class1;
sub new
{
my $type = {};
bless($self);
return $self;
}
sub DESTROY
{
print "Object is being destroyed!\n";
}
return 1;
Наследование : В следующем примере от базового класса Class1 порождается дочерний Class2.
В отличие от других объектно-ориентированных языков программирования,
в Perl нельзя объявить члены класса как private или как protected
package Class1;
sub new
{
my $self = {};
bless($self);
return $self;
}
sub gettext {return "Hello!\n";}
return 1;
package Class2;
use Class1;
@ISA = qw/Class1/;
sub new
{
my $self = {};
bless($self);
return $self;
}
return 1;
use Class2;
my $object1 = Class2->new();
print "The object says: ", $object1->gettext;
The object says: Hello!
Perl не проверяет структуру данных, создаваемую в конструкторе данного класса, даже на минимальную совместимость с методами,
которые наследуются от родительских классов. Совместимость данных и методов — целиком на совести программиста.
В этом примере конструктор класса Class2 можно унаследовать из класса Class1 (поскольку код Perl для этих конструкторов совпадает),
но так как конструктор класса Class1 и возвращает объект класса Class1, а не класса Class2, это вызвало бы проблемы.
Чтобы конструктор класса Class1 возвращал объекты класса Class2 (или любого другого, для которого Class1 является базовым),
при работе с ним в контексте дочернего класса, его необходимо переписать.
Для этой цели мы используем специальную форму функции bless с двумя аргументами, с которой мы еще не сталкивались.
package Class1;
sub new
{
my $class = shift;
my $self = {};
$self->{NAME} = "Christine";
bless $self, $class;
return $self;
}
return 1;
package Class2;
use Class1;
@ISA = qw/Class1/;
sub new
{
my $class = shift;
my $self = {};
$self->{DATA} = 200;
bless $self, $class;
return $self;
}
return 1;
use Class2;
my $object1 = Class2->new();
print $object1->{NAME}, " has \$", $object1->{DATA}, "\n";
Christine has $200
В Perl вопрос о том, является ли метод виртуальным или нет, решается просто: все методы, вызываемые как методы классов,
являются статическими. Наоборот, все методы, вызываемые как методы объектов, являются виртуальными.
Тем самым свойство «виртуальности» зависит от способа вызова, и один и тот же метод может быть как виртуальным, так и статическим. Пример:
package Class1;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub gettext{
return "Hello!\n";
}
sub printA
{
my $self = shift;
print $self->gettext; # виртуальный вызов
}
sub printZ
{
my $self = shift;
print Class1->gettext; # статический вызов
}
return 1;
package Class2; # конструктор наследуется
use Class1;
@ISA = qw/Class1/;
sub gettext{ # замещение метода
return "Bye...";
}
return 1;
Использование :
use Class1;
use Class2;
$object1 = Class1->new();
$object2 = Class2->new();
# статический вызов замещенного метода
$object1->printZ;
$object2->printZ;
# виртуальный вызов замещенного метода
$object1->printA;
$object2->printA;
Hello!
Hello!
Hello!
Bye...
Тот факт, что методы Perl, вызываемые как методы объекта, ведут себя как виртуальные методы традиционных языков программирования,
позволяет нам еще больше усовершенствовать схему конструкторов (см. выше раздел «Наследование конструкторов»).
А именно, разобьем конструктор на две части: метод new, который выделяет память под объект (анонимный хеш, анонимный массив или,
даже, анонимный скаляр) и привязывает ссылку к классу, и init, который инициализирует выделенную память.
Тогда мы можем замещать в дочерних классах только инициализирующую часть init, оставляя конструктор неизменным. Рассмотрим пример:
package Class1;
sub new
{
my $class = shift;
my $self = {};
bless $self, $class;
$self->init(@_);
return $self;
}
sub init
{
my $self = shift;
$self->{DATA} = shift;
return $self;
}
return 1;
package Class2; # конструктор new наследуется
use Class1;
@ISA = qw/Class1/;
sub init
{
my $self = shift;
$self->{DATA} = shift;
$self->{VALUE} = shift;
return $self;
}
return 1;
use Class1;
use Class2;
$object1 = Class1->new(1024);
$object2 = Class2->new(1024, 3.141592653589);
В Perl можно создавать переменные класса, — то есть, переменные, недоступные вне класса, и содержащие значения,
общие для всех объектов класса и доступные им. К сожалению, в Perl нет возможности организовать автоматическое
наследование переменных класса, если только вы не организуете доступ к переменным базового класса с помощью наследуемых методов доступа.
Единственный способ справиться с этой проблемой — это использовать для доступа к переменным класса только методы
доступа (то есть, никогда не обращаться к ним напрямую), и при создании дочернего класса вручную повторить в его теле
как переменные класса, так и методы доступа к ним. При этом в силу того, что методы объекта в Perl считаются виртуальными,
новые переменные класса будут использоваться независимо от того, работает с ними метод дочернего или же метод,
унаследованный от базового класса:
package Class1;
my $counter = 1;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub getcounter{
return $counter;
}
sub setcounter{
$counter = shift;
return $counter;
}
sub twicecounter{ # косвенный доступ
my $self = shift;
$my temp = $self->getcounter;
$temp = 2*$temp;
$self->setcounter($temp);
return $temp;
}
return 1;
package Class2; # конструктор наследуется
my $counter = 100;
use Class1;
@ISA = qw/Class1/;
sub getcounter{
return $counter;
}
sub setcounter{
$counter = shift;
return $counter;
}
sub triplecounter{ # косвенный доступ
my $self = shift;
$my temp = $self->getcounter;
$temp = 3*$temp;
$self->setcounter($temp);
return $temp;
}
return 1;
use Class2;
$object = Class2->new();
print $object->twicecounter, "\n";
print $object->triplecounter, "\n";
200
300
В Perl производный класс может наследовать сразу от нескольких классов.
Каждый из них должен быть объявлен с помощью команды use и перечислен в массиве @ISA.
package Class0;
sub printhi {print "Hi\n";}
return 1;
package Class1;
sub printhello {print "Hello\n";}
return 1;
package Class1;
use Class0;
use Class1;
$ISA = qw/Class0 Class1/;
sub new
{
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
return 1;
use Class2;
my $object = Class2->new();
$object->printhi;
$object->printhello;
Hi
Hello
|
Vourhey | Good. Thanks 2008-04-18 11:45:01 | |
|