2023/02/18

削除

この記事は削除されました。

2022/10/04
  1. Perl
  2. オブジェクト指向
  3. 入門

Perlオブジェクト指向プログラミング入門

Perlオブジェクト指向プログラミングの入門記事です。

クラスの定義

Perlではクラスの定義はpackageを使って行います。

package Point;

コンストラクタ

コンストラクタであるnewというクラスメソッドを定義します。bless関数を使って、オブジェクトを生成します。

package Point;

sub new {
  my $class = shift;
  
  # オブジェクトの生成
  my $self = bless {@_}, $class;
  
  # 初期化処理
  
  return $self;
}

コンストラクタを定義すれば、オブジェクトを作成することができます。

my $point = Point->new;

newメソッドは、ハッシュを受け取ることができます。

my $point = Point->new(x => 1, y => 2);

この値は、フィールドに設定されます。

初期化処理

コンストラクタでの初期化処理は以下のように書きます。

sub new {
  my $class = shift;
  
  # オブジェクトの生成
  my $self = bless {@_}, $class;
 
  # 初期化処理
  $self->{x} //= 0;
  $self->{y} //= 0;

  return $self;
}

//演算子を使って初期化しています。

アクセッサ

次にアクセッサを定義してみましょう。アクセッサとはフィールドにアクセスするためのメソッドのことです。アクセッサを定義すれば、フィールドにアクセスすることができます。

# xを取得・設定するアクセッサ
sub x {
  my $self = shift;
  
  if (@_) {
    $self->{x} = $_[0];
    return $self;
  }
  
  return $self->{x};
}

アクセッサを定義するとフィールドにアクセスすることができます。

# 値の設定
$point->x(1);

# 値の取得
my $x = $point->x;

継承

継承を行うにはbaseモジュールを使います。Pointを継承したPoint3Dを作ってみましょう。

package Point3D;

use base 'Point';

スーパークラスのメソッドの呼び出し

SUPER疑似クラスを使って、スーパークラスのメソッドを呼び出すことができます。

package Point3D;
use base 'Point';

# ...

sub clear {
  my $self = shift;
  
  # スーパークラスのclearメソッドの呼び出し
  $self->SUPER::clear;
}

コンストラクタのオーバーライド

サブクラスでコンストラクタをオーバーライドしたい場合があると思います。コンストラクタをオーバーライドするには次のようにします。

package Point3D;
use base 'Point';

sub new {
  my $class = shift;
  
  my $self = $class->SUPER::new(@_);
  
  # 初期化処理
  $self->{z} //= 0;

  return $self;
}

newはコンストラクタなので必ずオブジェクト自身を返すようにします。

クラスの作成 - 実践編

Perlのオブジェクト指向プログラミングを実践してみましょう。PointクラスとPoint3Dクラスを作成してみます。

Pointクラス

  • Pointは点を表すクラスです。
  • xとyというフィールドがあり、アクセッサを持ちます。
  • xとyの値を0にクリアするclearというメソッドを持ちます。
package Point;

sub new {
  my $class = shift;
  
  # オブジェクトの生成
  my $self = bless {@_}, $class;
 
  # 初期化処理
  $self->{x} //= 0;
  $self->{y} //= 0;

  return $self;
}

# xを取得・設定するアクセッサ
sub x {
  my $self = shift;
  
  if (@_) {
    $self->{x} = $_[0];
    return $self;
  }
  
  return $self->{x};
}

# yを取得・設定するアクセッサ
sub y {
  my $self = shift;
  
  if (@_) {
    $self->{y} = $_[0];
    return $self;
  }
  
  return $self->{y};
}

sub clear {
  my $self = shift;
  
  $self->x(0);
  $self->y(0);
}

1;

末尾に「1;」という記述があるのは、モジュールを書くファイルの末尾が真でなければならないというルールがあるためです。

Pointクラスは以下のように使用することができます。

use Point;
my $point = Point->new(x => 3, y => 5);
print $point->x;
$point->y(9);
$point->clear;

Point3Dクラス

  • Point3Dは3次元の点を表すクラスです。
  • x,y,zというフィールドがあり、アクセッサを持ちます。
  • x,y,zの値を0にクリアするclearというメソッドを持ちます。

Point3DPointを継承して作成します。clearメソッドはxyzの値をクリアするためにオーバーライドされています。

package Point3D;
use base 'Point';

# zを取得・設定するアクセッサ
sub z {
  my $self = shift;
  
  if (@_) {
    $self->{z} = $_[0];
    return $self;
  }
  
  return $self->{z};
}

sub clear {
  my $self = shift;
  
  $self->SUPER::clear;
  
  $self->z(0);
}

1;

Point3Dクラスは以下のように利用することができます。

use Point3D;
my $point = Point->new(x => 3, y => 5, z => 8);
print $point->z;
$point->z(9);
$point->clear;

実行できるサンプル

実行できるサンプルを書いておきますね。

lib/Point.pm

libというディレクトを作成して、その中にPoint.pmというファイルを作成してください。

package Point;

use strict;
use warnings;

sub new {
  my $class = shift;
  
  # オブジェクトの生成
  my $self = bless {@_}, $class;
 
  # 初期化処理
  $self->{x} //= 0;
  $self->{y} //= 0;

  return $self;
}

# xを取得・設定するアクセッサ
sub x {
  my $self = shift;
  
  if (@_) {
    $self->{x} = $_[0];
    return $self;
  }
  
  return $self->{x};
}

# yを取得・設定するアクセッサ
sub y {
  my $self = shift;
  
  if (@_) {
    $self->{y} = $_[0];
    return $self;
  }
  
  return $self->{y};
}

sub clear {
  my $self = shift;
  
  $self->x(0);
  $self->y(0);
}

1;

lib/Point3D.pm

libというディレクトの中にPoint3D.pmというファイルを作成してください。

package Point3D;
use base 'Point';

# zを取得・設定するアクセッサ
sub z {
  my $self = shift;
  
  if (@_) {
    $self->{z} = $_[0];
    return $self;
  }
  
  return $self->{z};
}

sub clear {
  my $self = shift;
  
  $self->SUPER::clear;
  
  $self->z(0);
}

1;

object.pl

クラスを読み込んで利用するサンプルです。libモジュールを使うことで、モジュールの検索パスを追加できます。

use strict;
use warnings;
use lib 'lib';
use Point3D;

my $point = Point3D->new(x => 3, y => 5, z => 8);

print $point->z . "\n";

$point->z(9);

print $point->z . "\n";
$point->clear;

print $point->z . "\n";

スクリプトの実行

ディレクトリ構成は以下のようにします。

object.pl
lib - Point.pm
    - Point3D.pm

以下のように実行してください。

perl object.pl

出力結果です。

8
9
0

オブジェクト指向プログラミングの詳細

オブジェクト指向プログラミングの詳細については、以下の記事を見てください。

2022/10/03

UNIVERSAL - すべてのオブジェクトのスーパークラス

UNIVERSALは、すべてのオブジェクトのスーパークラスです。UNIVERSALで定義されているメソッドは、すべてのオブジェクトから呼び出すことができます。

isa

isaメソッドは、あるオブジェクトが、引数で指定されたクラス、または、指定されたクラスを継承しているをチェックします。

# Pointまたは、Pointを継承
my $isa = $point->isa("Point");

# Point3Dまたは、Point3Dを継承
my $isa = $point->isa("Point3D");
2022/10/01

オーバーライド

オーバーライドとは、スーパークラスのメソッドを再定義することをいいます。

package Point;
  
sub move {
  my $self = shift;
  
  my ($x, $y) = @_;
  
  $self->{x} += $x;
  $self->{y} += $y;
}

package Point3D;

use base 'Point';

# moveメソッドをオーバーライド
sub move {
  my $self = shift;
  
  my ($x, $y, $z) = @_;
  
  $self->SUPER::move($x, $y);
  $self->{z} += $z;
}

オーバーライドの詳しい解説については、継承の記事を見てください。

2022/09/30
  1. Perl
  2. オブジェクト指向プログラミング
  3. here

クラスメソッド

Perlでは、クラスメソッドとは、クラス名を使ってメソッド呼び出しが行われるメソッドのことをいいます。クラスメソッドの定義では、第一引数にクラス名を受け取るようにします。

# クラスメソッド呼び出し
MyClass->foo(1, 2);

# クラスメソッドの定義
sub foo {
  my ($class, @args) = @_;
  
  
}

インスタンスメソッドも参考にしてください。

2022/09/29
  1. Perl
  2. オブジェクト指向プログラミング
  3. here

インスタンスメソッド

Perlでは、インスタンスメソッドとは、インスタンスからメソッド呼び出しが行われるメソッドのことをいいます。インスタンスメソッドの定義では、第一引数にインスタンスを受け取るようにします。単にメソッドとも呼ばれます。

# インスタンスメソッド呼び出し
$object->foo(1, 2);

# インスタンスメソッドの定義
sub foo {
  my ($self, @args) = @_;
  
  
}

クラスメソッドも参考にしてください。

2022/09/28
  1. Perl
  2. オブジェクト指向プログラミング
  3. here

mro - next::methodの有効化・メソッド解決順序の変更

mroモジュールは、多重継承における、スーパークラスのメソッド解決順序を変更するためのモジュールです。mroを読み込むと、next::methodが有効になります。多重継承で、スーパークラスのメソッドを探索する場合はSUPER疑似クラスよりも、next::methodうことが推奨されています。

# next::methodの有効化
use mro;

next::methodは、オブジェクトから呼び出します。

# next::methodの使い方
$self->next::method(@args);

next::methodの動作

next::methodは、C3というアルゴリズムで、スーパークラスを探索します。

C3 は常に局所的な優先順位を保存して動作します。 これは、基本的にどのクラスもそのサブクラスより先に現れることはないことを 意味します。 例えば、以下のような古典的なダイヤ型継承パターンを考えます:

     <A>
    /   \
  <B>   <C>
    \   /
     <D>

標準の Perl 5 MRO は (D, B, A, C) です。 この結果、C は A のサブクラスにも関わらず、A が C より先に 検索されます。 しかし、C3 MRO アルゴリズムでは、(D, B, C, A) の順序になり、この問題は ありません。

参考:C3 の動作 - mro

next::methodを多重継承で使ったサンプル

next::methodを多重継承で使ったサンプルです。

use strict;
use warnings;
use mro;

package MultiBase1 {
  sub b1_init {
    my $self = shift;
    
    push @{$self->{ps}}, 2;
    $self->{b1} = 3;
  }
}

package MultiBase2 {
  sub b1_init {
    my $self = shift;
    
    push @{$self->{ps}}, 7;
    $self->{b1} = 8;
  }
  
  sub b2_init {
    my $self = shift;
    
    push @{$self->{ps}}, 3;
    $self->{b2} = 4;
  }
}

package MultiClass {
  
  use base 'MultiBase1', 'MultiBase2';
  
  sub new {
    my $class = shift;
    
    my $self = bless {@_}, ref $class || $class;
    
    $self->{ps} //= [];
    
    $self->init;
    
    return $self;
  }
  
  sub init {
    my $self = shift;
    
    push @{$self->{ps}}, 1;
    
    $self->b1_init;
    $self->b2_init;
  }
  
  sub b1_init {
    my $self = shift;
    
    $self->next::method;
  }
  
  sub b2_init {
    my $self = shift;
    
    $self->next::method;
  }
}

my $object = MultiClass->new;

print $object->{b1} . "\n"; # 3

print $object->{b2} . "\n"; # 4

print "@{$object->{ps}}" . "\n"; # [1, 2, 3]

業務に役立つPerl

Perlテキスト処理のエッセンス

PerlでポータブルなLinuxファイル管理入門

ITエンジニアの求人情報など

 ITエンジニアの求人情報・Webサービス・ソフトウェア・スクールなどの情報。

システム開発のお問い合わせ