2007年03月11日 03:30 [Edit]

perl - Inside-out Object

ところが、そのないはずの隠蔽化を強制する仕組みがすでに発見されているのです。

ビンゴ中西のほげほげ Kansai.pm第8回ミーティングに行ってきました@荷造り中
そして、Perlのオブジェクト指向には 隠蔽化を強制する仕組みはない

いつか書こうと思っていたけど、ちょうどいい機会なので。


ここでは例として、name,uriという二つのインスタンス変数を持つオブジェクトを実装してみます。

InsideOut.pm
{
    package InsideOut;
    use strict;
    use warnings;
    my %objects;
    sub new {
        my $class = shift;
        bless \eval{ my $scalar }, $class;
    }
    sub DESTROY{
        my $self = shift;
        delete $objects{$self+0};
    }
    sub name{
       my $self = shift;
       $objects{$self+0}{name} = shift if @_;
       $objects{$self+0}{name};
    }
    sub uri{
       my $self = shift;
       $objects{$self+0}{uri} = shift if @_;
       $objects{$self+0}{uri};
    }
    1;
}
insideout.pl
use strict;
use warnings;
use InsideOut;
use Data::Dumper;

my $o = InsideOut->new();
$o->name("dankogai");
$o->uri("http://blog.livedoor.jp/dankogai/");
print $o->name, ",", $o->uri, "\n";
print Dumper $o;

ここで、insideout.plを実行してみると、以下のようになります。

% perl insideout.pl
dankogai,http://blog.livedoor.jp/dankogai/
$VAR1 = bless( do{\(my $o = undef)}, 'InsideOut' );

見ての通り、確かにaccessorを通した時には「インタンス変数」はアクセスできるのに、オブジェクトの中身は空っぽです。

ここで、InsideOut.pmの仕組みをよく見てみます。オブジェクトは空っぽのblessed scalar referenceです。Perlの場合、オブジェクトはblessed referenceであれば何でもOKなので、こうしています。

次にaccessorを見てみましょう。$objects{$self+0}{name}とは一体なんでしょう?$self+0に着目してみましょう。これは一体なんでしょうか?簡単なオブジェクトを作って実験してましょう。

% perl -MCGI -le '$q=CGI->new; printf "%s,%d,0x%x\n", $q, $q+0, $q+0'
CGI=HASH(0x1801180),25170304,0x1801180

そうなのです。リファレンスを数値コンテキストで評価すると、アドレスが返ってきます。これは各リファレンスに固有なので、オブジェクトのIDになります。accessorはこのIDをキーにしたハッシュ、%objectsにアクセスしています。

ところが、この%objectsはレキシカル変数です。InsideOut.pmの一番外側の{}の外からは全くアクセスできないのです。accessorを通すしかありません。これで、Perl 5でも隠蔽化が可能であることが示されました。

本来objectの中に入っているデータが外にある。それ故この方式で実装されたオブジェクトを、Inside-Out Objectと呼びます。

一つ留意しなければならないのは、この方式の場合、DESTROYを必ず実装しておく必要があります。データはオブジェクトの中にはないので、オブジェクトが破棄される際に、それに紐づいたデータは、%objectsが見えるスコープ中のDESTROYで削除してやる必要があるのです。

この方式はDamian Conwayのお気に入りで、それが嵩じて彼が実装したのがClass::Stdです。Inside-Out Objectにまつわる不便を、まとめて面倒みてくれるというものです。名前がClass::Stdとなっているところに彼のHubrisを感じずにはいられません。

詳しくは、上掲のPerl Best Practicesをご覧あれ。

Dan the Inside-Out Blogger


この記事へのトラックバックURL

この記事へのトラックバック
先の記事で「パッケージ」でインポートした関数などを、「クラス」で使う方法を書いた。本稿では同方法で、「クラス変数」をどのように実現するか例示する。
Perl でオブジェクト指向 C 風 その2 クラス変数【JRF のソフトウェア Tips】at 2011年01月06日 22:27
PERL HACKS(日本語版) [英語版] うーん、さすがにそれはいいすぎでしょうか。 クロージャの概念をクラスとの対比でわかりやすく説明する。 - サンプルコードによる Perl 入門ここで気づいてほしいことは、クラスとクロージャは、実は同じものだということです。
perl - Class vs. Closure【404 Blog Not Found】at 2008年07月13日 03:31
Perl の Inside-out Object を C# に応用してみました...
C# で Inside-out Object っぽいこと【tsucchi's tech note】at 2007年11月12日 23:23
nside-Out Objectの原型と、Object::InsideOutの紹介。
Object::InsideOut【つんのめりそうな日々】at 2007年03月13日 00:09
オブジェクト指向Perlマスターコース―オブジェクト指向の概念とPerlによる実装方法(ダミアン コンウェイ) 今手元に無いので確認できないが、似たような手法は オブジェクト指向Perlマスターコースにも載っていた。たぶん。 詳しくは、上掲のPerl Best Practicesをご覧あれ...
[tech][book] Perl のインスタンス変数隠蔽化【ぷーたろ日記】at 2007年03月11日 22:17
この記事へのコメント
「なんでこのクラス、オブジェクトのリストをクラス(パッケージ)変数で一旦持ってるの?しかもご丁寧にDESTROYがあるからスコープ外れたら消えるし」
「さあ、どっかで使うんじゃない?」
・・・(この間1時間〜1週間)・・・
「各オブジェクトのプロパティ隠蔽化のためだってさ」
「知らなきゃわかるかぁ!」
・・・ええ、ええ、「読めない」のは勉強不足のせいです。
毎度の啓蒙、ありがとうございます。
Posted by osam at 2007年03月12日 10:34
そしてそして、ますますperlは「読めない」言語へと進化するのでした。
Posted by osam at 2007年03月12日 10:21
すみません、いつもいろんな良書をこちらのAmazonのリンクから購入していますが、Perl Best Practiceについてはタイミングがずれて、別のBlogから購入してしまいました。今後、Perl本をAmazonから買うときはまずこちらのBlogをチェックしてからにします。
それにしてもPerlは奥が深くて、Blogを読むたびに「こんな書き方ができるのか・・・」と、驚きを隠せません。感謝です。
Posted by 風晶 at 2007年03月11日 19:38
わたしは、クラスのインスタンスがハッシュリファレンスでないものにデータを持たせるのに使っていました。
Wx::FileDialogなどですが・・・
Class::InsideOutがオススメです。
http://search.cpan.org/~dagolden/Class-InsideOut-1.06/lib/Class/InsideOut/Manual/About.pod
の下「Other modules on CPAN」にClass::Std含め、CPAN上のInsideOut系の寸評一覧があります。
Posted by ぼくちん at 2007年03月11日 16:34