読者です 読者をやめる 読者になる 読者になる

はむはむエンジニアぶろぐ

このブログのコンセプトは"ハッキングの為なら愛する家族を傷つけることをいとわない" 自分にとってエンジニアリングは "手段ではなく生きる目的" である

Perl setterとgetterを自動生成するAccessor(アクセサー)を使ってみる

f:id:secret_hamuhamu:20150426020452p:plain

Perlで、setterとgetterを作成する際、メンバ変数ごとに用意していましたがAccessorを使えば省略できるとのこと。
覚えると結構使用頻度高そう。

環境構成


ソースコード

モジュール

身長と体重のsetterとgetterをもつHumanクラス。

package Human;

use strict;
use warnings;
use base qw/Class::Accessor/;

__PACKAGE__->mk_accessors(qw/ height  weight/);

1;
__END__


このHumanクラスにnewはなく、Class::Accessorというモジュールがオーバライドしている。
mk_accessorsにsetterとgetterを自動生成したいメンバ変数を指定する。

これでheightとweightにアクセスできるインターフェースが出来た。
実行スクリプトで、使ってみる。


実行スクリプト

use 5.16.0;
use strict;
use warnings;
use Human;
use Data::Dumper;

# newメソッドで、セットできる
my $human = Human->new({
    height => 170,
    weight => 55,
});

# メンバ変数名で参照
say $human->height;
say $human->weight;
# 170
# 55


$human->height(160);
$human->weight(45);

say $human->height;
say $human->weight;
# 160
# 45

# setメソッドでセットできる
$human->set('height', 180);
$human->set('weight', 65);

# getメソッドでゲットできる
say $human->get('height');
say $human->get('weight');
# 180
# 65

# 配列でゲット出来る
say Dumper $human->get(qw/ height weight /);
# $VAR1 = 180;
# $VAR2 = 65;


だいたい使い方は、こんな感じ。
ただ、getterはともかく。
ガバガバなsetterは、使いたくないものである。

setterのみ自動生成できないか調べてみたところ発見した。
mk_ro_accessor を使う。


モジュール

package Human2;

use strict;
use warnings;
use base qw/Class::Accessor/;

__PACKAGE__->mk_ro_accessor(qw/ name /);

1;
__END__

実行スクリプト

use Human2;
my $human2 = Human2->new({name => 'はむはむ'});
say $human2->get('name');
# はむはむ

# セットできない
$human2->name('ばむばむ');

1;
__END__


読み取り専用のメンバ変数にセットしようとすると例外になります。

バグ?仕様?

先ほどの、mk_ro_accessorですが実は完全な読み取り専用では、ないらしい。
setメソッドを使えば、値を変更されてしまうのだ。

$human2->set('name', 'ばむばむ');
say $human2->get('name');
# ばむばむ

これに関しては、理解不能である。
ドキュメントを見る限り値をセットできないと来てあるが。。。
ネットで調べても関連記述がすぐに見つからなかったのでソースコード読むしか無さそう。

mk_ro_accessorは、使えないけど Class::Accessor::Lite というモジュールは、もっと簡単に使えるようなのでそちらを使ってみる。

Class::Accessor::Liteを使ってみる

モジュール

package Human2;

use strict;
use warnings;
use base qw/Class::Accessor::Lite/;
use Class::Accessor::Lite(
    new => 1, # デフォルトコンストラクタ作成
    rw  => [ qw(weight) ],
    ro  => [ qw(name) ],
    wo  => [ qw(height) ],
);

1;
__END__

かなり分かりやすい。
rw ならsetterとgetterが使える。
ro ならsetterのみ。
wo ならgetterのみ。

実行スクリプト

use Human2;
my $human2 = Human2->new({name => 'はむはむ'});
say $human2->name;

# 値をセットできない
# $human2->name('ばむばむ');

# セットできない
# $human2->set('name', 'ばむばむ');

# getメソッドは使えない
# say $human2->get('name');

1;

__END__

先ほどの、 mk_ro_accessor とは違い get すると例外になる。
ただ、 set も使えなくなっていてメンバ変数名でのみしかアクセス出来ない。

setかgetか指定できたほうが意図が明確なので、好きなのですが Class::Accessor::Lite を使っていこうと思います。

最近読んだ本

初めてのPerl 第6版

初めてのPerl 第6版

続・初めてのPerl 改訂第2版

続・初めてのPerl 改訂第2版

Perlベストプラクティス

Perlベストプラクティス

Perl Hacks ―プロが教えるテクニック & ツール101選

Perl Hacks ―プロが教えるテクニック & ツール101選

Perl テスティング ハンドブック

Perl テスティング ハンドブック