InsideOut

InsideOut

Perlを使う会社が新人にどこまで教えるかの指標としてClass::Data::Inheritableを説明できる、という事を目安にしているという記事なんですが

http://d.hatena.ne.jp/fbis/20060510/1147253289

なんだかんだいって独学でそこにたどり着くのはしんどいな、と思っていたところ、InsideOutのコードを(勉強会のネタがなかったので苦し紛れに)書いたところ、Class::Data::Inheritableを説明する前にInsideOutを説明できるようになる、をそれより前にある小さな目標にするにいいと思った、という記事です。

InsideOutとは

オブジェクトの(ほぼ)完全なカプセル化が目的の手法。アクセッサ経由でないとアクセスできないレキシカル変数に対して、そのオブジェクト自身のメモリ番地をKeyとして値を格納する領域を用意してあげる。結果、その領域には、そのオブジェクトが、そのパッケージのメソッドを経由しないとアクセスできない。という説明であってるだろうか

SEE ALSO: http://blog.livedoor.jp/dankogai/archives/50783623.html

対象となる人

  • ゲッターとセッターについて質問しない人
  • クラス変数とインスタンス変数の違いが分かる人
  • クロージャ、もしくはリファレンスカウントの概念が分かるひと
  • blessされた変数を数値評価すると、メモリのアドレスが返ってくる、の説明に納得してくれる人

lib/InsideOut.pm

package InsideOut;
use strict;
use warnings;

{
    my %name_of;
    my %uri_of;

    sub new {
        my $class = shift;
        bless \do{ my $scalar }, $class;
    }

    sub DESTROY {
        my $self = shift;
        my $address = $self->_address; 
        delete $name_of{$address};
        delete $uri_of{$address};
        return;
    }

    sub name {
       my $self = shift;
       my $address = $self->_address; 
       $name_of{$address} = shift if @_;
       $name_of{$address};
    }
    
    sub uri {
       my $self = shift;
       my $address = $self->_address; 
       $uri_of{$address} = shift if @_;
       $uri_of{$address};
    }

    sub _address {
        my $self = shift;
        my $address = $self + 0;
    }
}

1;

eg/inside_out.pl

use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/../lib";
use InsideOut;
use Data::Dumper;

my $o  = InsideOut->new();
my $o2 = InsideOut->new();

### set
$o->name("okamura");
$o->uri("http://example.com/");

### get
print $o->name, "\n";
print $o, "\n";

# blessされた変数には何も情報がないように見えるが
# blessされた変数の住所を利用するトリックなのです
print Dumper $o;  
print $o->_address, "\n";

解説

%name_of, %uri_of

クラス変数を宣言しています。中括弧で囲われているスコープ内でしかこの変数は生きられませんが、サブルーチンがこの変数を参照しているのでこのサブルーチン(識別子から辿れる)から参照している限り、一応行き続けることができます。

最もアクセスする場合はそのサブルーチン(アクセッサ)経由に限定されるわけですが、それこそ狙いなのです。

new()

無名リファレンスをblessしています。実装でよく利用されるのはハッシュリファレンスですが、このインスタンスには情報を記録しないで前述のクラス変数に値を格納します。よって最もコストの少ないスカラーリファレンスをブレスしています。

DESTROY()

インスタンス化した変数が消失すると、このDESTROYが呼び出されて実行されます。インスタンスが消えてもクラス変数にはそのまま値が残ってしまうのでインスタンスが消えるたびにメモリからデータを消してあげないとメモリリークがおきてしまいます。

name(), uri()

セッターとゲッターです。ここを経由しないとデータを取り出したり、格納したりできません。ここを操作するのはインスタンスのみなのでクラスメソッドとして呼ぶとCarp::croakで例外を送出したほうがよいかも

_address()

自分自身(スカラーリファレンス)に0を足す事によって数値評価をしています。結果この返り値はメモリのアドレスが返されます。

まとめ

InsideOutはPBPが推奨してるかもしれないけれど、
俺たちにはもっとプライオリティの高い作業があると思う。

使っている人に会ったことないという。

まあ勉強にはいいんじゃないかと。