さわだのノート

書籍のお仕事に役立つかもしれない思いつきを記録しています。

RSS: 記事の更新情報 Rss Feed

Perlでsetter/getterを楽に(動的に)定義する

簡単だけど面倒なPerlオブジェクト指向プログラミング

Perlオブジェクト指向プログラミングは、「リファレンス」さえわかっちゃえば簡単に使える分、巷で言われているよりは意外ととっつきやすいんじゃないかと思います。

しかし、とっつきやすいのはいいんですが、オブジェクトの大本であるハッシュリファレンスを操作するためのメソッドを書くのが面倒くさい。
いや、データを操作するためのプログラムを書くのはそりゃしょうがないんですが、値を入れる・取得するだけのメソッド(いわゆるsetter/getter)までいちいち用意しないといけないのはちょっと面倒です。

そこで、このメソッドを動的に生成できないかと思って、Googleで検索してみました。
関数テンプレートを使って動的に複数のサブルーチンを作成する - サンプルコードによるPerl入門

どうやら動的にサブルーチンを定義する仕組みがあるみたいですね。
無名サブルーチンと型グロブを利用することで、サブルーチンを動的に生成し、名前をつけていくみたいです。意味がわかりません。eval使うとかじゃないんだ……。

なんか「no strict」とか書かないといけないし、面倒だなあと思っていたら、上記の記事に

実際の利用としては、Class::Accessor などのモジュールで、アクセッサーの作成の部分で利用されています。

と書かれているではないですか。
小難しいことは抜きにして、このモジュールを使えば、簡単に「setter/getter」の定義ができちゃうわけですね。

Class::Accessorモジュールを使う

Class::Accessorモジュールの基本的な使い方はいたって簡単です。Class::Accessorパッケージを継承して、mk_accessorsメソッドにアクセサを定義したい項目をリストで渡してあげればいいみたいです。

package Foo;
use parent qw(Class::Accessor);
__PACKAGE__->mk_accessors( qw/far bar car/ );

これでFooクラスに「far」「bar」「car」メソッドが作られたことになります。

もう少しプログラムらしくするなら、

package My::Test;
use strict;
use warnings FATAL => qw/all/;
use utf8;
use parent qw/Class::Accessor/;

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

__PACKAGE__->mk_accessors(qw/one two three four five/);

1;

package main;
use v5.16;
use warnings FATAL => qw/all/;
use utf8;
use open IO => qw/:utf8 :std/;

my $test = My::Test->new(
  one   => 1,
  two   => 2,
  three => 3,
  four  => 4,
  five  => 5,
);

say $test->one;
say $test->two;

$test->one(100);
say $test->one;

こんな感じでしょうか。オブジェクトの大本であるハッシュリファレンスのキーと、アクセサの項目名は一致させておく必要があります。
実行結果はこんな感じ。

1
2
100

ちゃんとsetter/getterとして機能しています。

アクセサの項目名も入れるのが面倒であれば(あとで何か追加するかもしれないし……)、

package My::Test;
use strict;
use warnings FATAL => qw/all/;
use utf8;
use parent qw/Class::Accessor/;

sub new {
  my ($class, %self) = @_;
  bless \%self => $class;
  $class->mk_accessors(keys %self);
  return \%self;
}

1;

こんな感じで定義してもちゃんと動いてくれます。

実はもっと細かく設定もできますし、当然のごとくメソッドのオーバーライドも可能です。
Perlを勉強した本にはこのモジュールは紹介されていなかったので、ごりごりコピペしながらメソッドを書いておりました。
RubyPythonがうらやましいなあ」なんてことも思ったりしたわけですが、こんな簡単にアクセサが設定できるなら、Perlでも全然戦えそうですね。