Perl オブジェクト指向


Perl にはオブジェクト指向プログラミングの 2 つの異なる実装があります:

  • 1 つは匿名ハッシュ テーブルに基づいており、各オブジェクト インスタンスの本質は匿名ハッシュ テーブルを指す参照です。この匿名ハッシュ テーブルには、すべてのインスタンス属性が保存されます。

  • 2 つ目は、配列メソッドに基づいており、クラスを定義するときに、各インスタンス属性の配列を作成します。各オブジェクト インスタンスの本質は、これらの配列内の行インデックスを指す参照です。これらの配列には、すべてのインスタンスのプロパティが保存されます。


オブジェクト指向の基本概念

オブジェクト指向の基本概念はたくさんありますが、ここではオブジェクト、クラス、メソッドの 3 つを受け入れます。

  • オブジェクト: オブジェクトは、クラス内のデータ項目への参照です。 .

  • Class: クラスは、オブジェクト メソッドを提供するクラスを含む Perl パッケージです。

  • メソッド: メソッドは Perl サブルーチンであり、クラス名はその最初のパラメーターです。

Perl は bless() 関数を提供し、bless はオブジェクトを構築するために参照をクラス名に関連付け、その参照を返します。


クラスの定義

クラスは単なるパッケージです。

パッケージをクラスとして使用したり、パッケージ内の関数をクラスのメソッドとして使用したりできます。

Perl パッケージは独立した名前空間を提供するため、異なるパッケージ内のメソッドと変数名が競合することはありません。

Perl クラスのファイル拡張子は .pm です。

次に、Person クラスを作成します。

package Person;

クラスのコード範囲は、スクリプト ファイルの最後の行、または次のパッケージ キーワードの前に達します。


オブジェクトの作成と使用

クラスのインスタンス (オブジェクト) を作成するには、コンストラクターを定義する必要があります。ほとんどのプログラムはコンストラクターとしてクラス名を使用します。Perl では任意の名前を使用できます。

さまざまな Perl 変数を Perl オブジェクトとして使用できます。ほとんどの場合、参照配列またはハッシュを使用します。

次に、Perl のハッシュ参照を使用して、Person クラスのコンストラクターを作成します。

オブジェクトを作成するときは、オブジェクトへの参照を返すサブルーチンであるコンストラクターを提供する必要があります。

例は次のとおりです:

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 のメソッド定義は特別な構文を提供しませんが、メソッドの最初のパラメータが参照されるオブジェクトまたはパッケージであることを規定しています。

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( "小明", "王", 23234345);
# 获取姓名
$firstName = $object->getFirstName();

print "设置前姓名为 : $firstName\n";

# 使用辅助函数设置姓名
$object->setFirstName( "小强" );

# 通过辅助函数获取姓名
$firstName = $object->getFirstName();
print "设置后姓名为 : $firstName\n";

上記のプログラムを実行すると、出力結果は次のようになります:

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

継承

Perl では、クラス メソッドは @ISA 配列を通じて継承されます。この配列には、他のパッケージ (クラス) の名前が含まれます。変数の継承は明示的に設定する必要があります。

多重継承とは、@ISA 配列に複数のクラス (パッケージ) 名が含まれていることを意味します。

メソッドは @ISA を通じてのみ継承でき、データは継承できません。

次に、Person クラスを継承する Employee クラスを作成します。

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( "小明", "王", 23234345);
# 获取姓名
$firstName = $object->getFirstName();

print "设置前姓名为 : $firstName\n";

# 使用辅助函数设置姓名
$object->setFirstName( "小强" );

# 通过辅助函数获取姓名
$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( "小明", "王", 23234345);
# 获取姓名,使用修改后的构造函数
$firstName = $object->getFirstName();

print "设置前姓名为 : $firstName\n";

# 使用辅助函数设置姓名
$object->setFirstName( "小强" );

# 通过辅助函数获取姓名
$firstName = $object->getFirstName();
print "设置后姓名为 : $firstName\n";

上記のプログラムを実行した後の結果は、デフォルトで次のようにロードされます:

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

要求されたメソッドが現在のクラス、現在のクラスのすべての基本クラス、および UNIVERSAL クラスで見つからない場合、 このとき、AUTOLOAD() という名前のメソッドが再度検索されます。 AUTOLOAD が見つかった場合は、 グローバル変数 $AUTOLOAD を呼び出し、その値を欠落しているメソッドの完全修飾名に設定します。

これが機能しない場合、Perl はエラーで失敗します。

基本クラスの AUTOLOAD を継承したくない場合は、非常に簡単で、たった 1 文です:

sub AUTOLOAD;

デストラクターとガベージ コレクション

オブジェクトの最後の参照が解放されると、オブジェクトは自動的に破棄されます。

破棄中に何かをしたい場合は、クラス内に「DESTROY」という名前のメソッドを定義できます。適切なタイミングで自動的に呼び出され、必要に応じて追加のクリーンアップ アクションを実行します。

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

Perl はオブジェクト参照を次のように扱います。 渡される引数は DESTROY のみです。この参照は読み取り専用であることに注意してください。つまり、$_[0] にアクセスして変更することはできません。 (翻訳者注: perlsub を参照) ただし、オブジェクト自体 ("${$_[0]" など) または、「@{$_[0]}」や「%{$_[0]}」など)は引き続き書き込み可能です。

デストラクターが戻る前にオブジェクト参照を再度祝福すると、Perl はデストラクターが戻った後に再祝福したオブジェクトの DESTROY メソッドを呼び出します。これにより、基本クラスまたは指定した他のクラスのデストラクターを呼び出すことができます。 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 called\n";
   my $type = shift;            # 包名
   my $self = MyClass->new;     # 引用空哈希
   return bless $self, $type;  
}

sub DESTROY
{
   print "MySubClass::DESTROY called\n";
}

sub MyMethod
{
   my $self = shift;
   $self->SUPER::MyMethod();
   print "   MySubClass::MyMethod called!\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