1. Perl
  2. オブジェクト指向
  3. コンストラクタの作成

コンストラクタ

前回は以下のようなクラスの雛形を作成しました。

package Book;

sub new { # コンストラクタの実装 }

sub title { # アクセッサの実装 }
sub author { # アクセッサの実装 }

1. コンストラクタとは

オブジェクトはクラスを元に生成されます。オブジェクトとは何かとかクラスとは何かということは少し忘れて、「オブジェクトはクラスを元に生成される」ということを覚えてください。

そしてコンストラクタとはオブジェクトを生成するメソッドのことです。コンストラクタを作成すれば

my $book = Book->new;

という記述でオブジェクトを作成できるようになります。

コンストラクタ名には慣習的にnewという名前が使用されます。ですが、コンストラクタはnewという名前に必ずしましょう(なぜならあなたのモジュールを使う人はコンストラクタ名がnewであることを期待するからです)

2. 一番簡単なコンストラクタの実装

ではコンストラクタを実装してみましょう。以下が一番簡単なコンストラクタの実装になります。でも一番簡単といっても少し難しいのです。

sub new { 
    my $class = shift;
    my $self = {};
    return bless $self, $class;
}

以下の呼び出し

my $book = Book->new;

を行ったとき、 $class には、何が代入されるでしょうか?

答えは、Book です。コンストラクタnewは第1引数としてクラス名を受け取ります。

3. ハッシュのリファレンスを準備する

  my $self = {};

の行は何をしているのでしょうか?

実はこれはオブジェクトが所有するデータなのです。重要なのでもう一度いいます。オブジェクトはデータを所有します。そして、一般的には、ハッシュのリファレンスがデータとして選択されます。

(本当に本当のことをいえば、オブジェクトはデータそのものです。ややこしくならないために最初はオブジェクトはデータを所有すると覚えてください。)

4. データとクラスを結びつける

最後の行

return bless $self, $class;

には、bless という関数があります。

bless関数は、第1引数にデータ、第2引数にクラス名を受け取って、データとクラスを結び付けます。

オブジェクトは、データとクラスを結びつけて生み出されるのです。そして、結びつけたものを呼び出しもとに返却してあげます。

5. Perlでのオブジェクトとは何か?

Perlのオブジェクトとは、クラスに結び付けられたデータのことです。それ以上の意味もそれ以下の意味もありません。

そしてこのように作ったオブジェクトは、他の言語で使用されるオブジェクトと遜色ない機能を発揮します。

コンストラクタに引数を渡せるようにする

今回は、コンストラクタに引数を渡せるようにしてみましょう。

1. コンストラクタに引数を渡す。

オブジェクトを作成するときは

my $book = Book->new(title => 'Good news', author => 'Kimoto');

という形が理想的です。

このような機能を持つコンストラクタを作成してみましょう。

sub new {
  my ($class, %args) = @_;
  my $self = {%args};
  return $self, $class;
}

特に難しい部分はありませんね。

2. ハッシュのリファレンスも受け入れたい

ハッシュを受け入れられると同時にハッシュのリファレンスも受け入れたいと思うところでしょう。こんな感じに。

my $book = Book->new({title => 'Good news', author => 'Kimoto'});

対応できるコンストラクタは、こんな感じです。

sub new{
    my ($class, @args) = @_;
    my %args = ref $args[0] eq 'HASH' ? %{ $args[0] } : @args;
    my $self = {%args};
    return $self, $class;
}

第1引数が、ハッシュリファレンスだった場合は、それをデリファレンスしてハッシュに代入しますそうでない場合は、引数をハッシュとして代入します。

3. コンストラクタにデフォルト値を設定したい

さてさて、コンストラクタに対する要求はまだまだ続きます。値が代入されない場合のデフォルト値を設定したい場合などです。

sub new {
  my ( $class, @args ) = @_;
  my %args = ref $args[0] eq 'HASH' ? %{$args[0]} : @args;
  my $self = {%args};
 
  $self->{title} ||= 'default title';
  $self->{author} ||= 'default author';

  return $self, $class;
}

以下の部分に注目してください。値が偽値ならば、デフォルト値を代入するという操作をしています。

  $self->{ title } ||= 'default title';
  $self->{ author } ||= 'default author';

でもこれじゃ、空文字を代入された場合もデフォルト値が設定されてしまいますので、それがまずい場合は、defined 関数を使用して

  $self->{ title } = 'default title' unless defined $self->{ title };

とします。

汎用的なコンストラクタの雛形

汎用的なコンストラクタの雛形を提示しておきます。汎用的なコンストラクタの雛形はちょっと難しいです。

1. 汎用的なコンストラクタの指針

汎用的なコンストラクタは、クラスから呼ばれたときだけではなくて、オブジェクトから呼び出された場合も考慮に入れる必要があります。

クラスから呼ばれた場合と

my $book = Book->new;

オブジェクトから呼ばれた場合の

my $book2 = $book->new

の両方に対応する。

またもうひとつの指針として、コンストラクタと初期化処理は分離するということが挙げられます。初期化用のinitメソッドを作成してあげます。

2. 汎用的なコンストラクタの作成

以下が汎用的なコンストラクタの雛形です。

sub new {
  my $proto = shift;
  my $class = ref $proto || $proto;
  my $self = {};
  bless $self, $class;
  
  $self->init(@_);
  return $self;
}

sub init {
  my ($self, @args) = @_;
  # 追加で行いたい処理
}

3. オブジェクトからクラス名を取り出す処理

この部分がわかりにくいと思います。

  my $proto = shift;
  my $class = ref $proto || $proto; 

これは何をやっているかというと、$proto がオブジェクトだった場合には、ref関数を使って関連付けられているクラス名を取り出します。

そうでない場合は、クラス名をそのまま使用するということをやっています。ref関数に文字列(クラス名)が渡された場合は、偽値が返るので、|| の右側が実行されます。

そういうわけで、クラス名から呼ばれても、オブジェクトから呼ばれても、$classにはクラス名が入ることになります。

4. 初期化処理を分離する

初期化処理を分離しているのが以下の部分です。初期化を行う場合は、bless 関数で先にオブジェクトを作成しておいてあげます。そしてそのオブジェクトから、initメソッドを呼び出します。

ちょっとわかりにくいですが、慣れましょう。同じクラスのメソッドを呼び出すには、オブジェクトつまり、bless された $self から呼び出す必要があるのです。

  my $self = {};
  bless $self, $class;
  $self->init(@_);

5. なぜこのように作成する必要があるのか?

それはきちんとはまだいいません。継承のところで取り上げることにします。このようにオブジェクトを作成しておかないと、継承をしようとしたときに面倒な問題が発生するとだけいっておきます。

業務に役立つPerl

関連情報