Slide 1

Slide 1 text

Perlにおけるクラスの実装パター ン 2018/11/22 吉祥寺pm#16 1 / 23

Slide 2

Slide 2 text

自己紹介 id : @_ybrliiu 新卒エンジニア CGIゲームがきっかけで学生の頃からPerl書いてました 2 / 23

Slide 3

Slide 3 text

今日話すこと Perlにはクラスの実現方法がいろいろあって、ハッシュベース のクラス以外にも様々な実現方法があるので、 それらの実装 方法、特徴、用途などを紹介します クラスビルダーの話はしません 3 / 23

Slide 4

Slide 4 text

前提知識 : PerlのOOP Class構文はない packageをClassとして扱う instanceはリファレンスにpackage名をbless関数で紐付か せたもの constructorはinstanceを新しく作る関数 methodはpackageに定義されている、instanceを第1引数 に受け取る関数 4 / 23

Slide 5

Slide 5 text

ハッシュベースのクラス ハッシュリファレンスにパッケージ名を紐付かせる インスタンス変数はハッシュリファレンスの要素に格納 最も一般的なクラスの実現方法で、大体のクラスビルダーは これがベース package Point; sub new { my ($class, $x, $y) = @_; bless +{ x => $x, y => $y }, $class; } sub x { my $self = shift; $self->{x}; # インスタンス変数 } 5 / 23

Slide 6

Slide 6 text

メリット 実装が簡単 理解しやすい 拡張性がある デバッグが容易 比較的速度が速い 6 / 23

Slide 7

Slide 7 text

デメリット 比較的メモリ使用量が多い カプセル化が不十分 メソッドを介さずとも容易にインスタンスの状態を変更 することができる 自由に値を追加 / 削除 / 変更できる構造体をプログラ ムの中で引きづり回せると考えると、厳しい物がある my $point = Point->new(-3, 5); $point->{x} = 100; # accessorを介さなくてもインスタンス変数を操作できてしまう say $point->x; # -> 100 $point->{name} = 'Anonymous'; # こんなこともできてしまうわけです 7 / 23

Slide 8

Slide 8 text

ハッシュ以外のデータ構造のリファレンスでもクラスは実現できま す!!! 8 / 23

Slide 9

Slide 9 text

配列ベースのクラス 配列ファレンスにパッケージ名を紐付かせる インスタンス変数は配列リファレンスの要素 大昔からあるモジュールでよく使われている Time::Piece, File::stat etc... package Point; sub new { my ($class, $x, $y) = @_; bless [$x, $y], $class; } sub x { my $self = shift; $self->[0]; # インスタンス変数 } 9 / 23

Slide 10

Slide 10 text

メリット メモリ使用量が少ない インスタンス変数へのアクセス速度が速い 10 / 23

Slide 11

Slide 11 text

デメリット 拡張しづらい 継承したときとかが大変で、親クラスはinstance変数 として添字をn番まで使っていたから〜〜などと考える 必要がある 多重継承やRoleでmixinみたいなことをしだすともは や制御不可能 少しデバッグしづらくなる カプセル化が不十分 11 / 23

Slide 12

Slide 12 text

サブルーチンベースのクラス サブルーチンリファレンスにパッケージ名を紐付かせる サブルーチンリファレンスを実行することでインスタンス変数 を取得する 昔のjsにもこういうのがありましたね package Point; sub new { my ($class, $x, $y) = @_; my $fields = +{ X => $x, Y => $y }; bless sub { $fields->{shift(@_)} }, $class; } sub x { my $self = shift; $self->('X'); # サブルーチンリファレンスを実行してインスタンス変数として扱えるものを取り出 } 12 / 23

Slide 13

Slide 13 text

メリット きちんとカプセル化されている 実装しているクラスだけが知っているはずのインスタン ス変数の取り出し方法がわからない限り 13 / 23

Slide 14

Slide 14 text

デメリット メモリ使用量が多い 毎回関数を実行するので、インスタンス変数へのアクセス速 度が遅い 少しデバッグしづらくなる 14 / 23

Slide 15

Slide 15 text

スカラベースのクラス スカラリファレンスにパッケージ名を紐付かせる インスタンス変数は紐付けたスカラリファレンスの実体のみ スカラ値をラップするだけのようなモジュールや、XSモジュー ルなどで使われていたりする boolean, Furl, URI etc... package PositiveInt; sub new { my ($class, $num) = @_; die 'PositiveInt must be larger than 0.' if $num < 0; bless \$num, $class; } sub num { my $self = shift; $$self; # デリファレンスすることでインスタンス変数として使える } 15 / 23

Slide 16

Slide 16 text

メリット メモリ使用量が非常に少ない 16 / 23

Slide 17

Slide 17 text

デメリット 拡張性が低い 17 / 23

Slide 18

Slide 18 text

Insideout Object スカラリファレンスベースのクラスを応用したもの インスタンスのアドレスを数値として評価し、それをキーとして package localなハッシュ変数などにインスタンス変数を格納 メモリを開放する処理を自前で実装しなければならない インスタンスが必要なくなってもGCがメモリを開放する のはインスタンスの領域だけで、 各インスタンス変数の データはpackage localな変数に残り続ける Perl Best Practiceで推奨されている方法だが、あまり普及し なかった 18 / 23

Slide 19

Slide 19 text

実装 package Point; my (%x_fields, %y_fields); sub new { my ($class, $x, $y) = @_; my $ref = \(my $anon) + 0; $x_fields{$ref} = $x; $y_fields{$ref} = $y; bless $ref, $class; } sub x { my $self = shift; $x_fields{$$self + 0}; # インスタンス変数 } sub DESTROY { my $self = shift; my $ref = $$self + 0; delete $x_fields{$ref}; delete $y_fields{$ref}; } 19 / 23

Slide 20

Slide 20 text

メリット インスタンス変数を完全にクラスの外部から隠蔽している 拡張性がある カプセル化を実現しているクラスの実装パターンのなかでは 速度が最も速い 20 / 23

Slide 21

Slide 21 text

デメリット インスタンス変数の呼び出しに比較的時間がかかる 毎回blessしているオブジェクトのアドレスを数値に変 換する手間がかかるため 高速化のテクニックなどもあるが、工夫してもハッシュベ ースのインスタンスと比べて2倍ほどかかってしまう この実装パターンを知らない人にとってはコードが何をやって いるのかかなり理解しにくい メモリ開放処理を自前で実装しなければならない そのままではデバッグがしづらい 21 / 23

Slide 22

Slide 22 text

その他の方法 グロブリファレンス、ファイルハンドルリファレンス、正規表現リ ファレンス すべてクラスとして扱えます リファレンスをラップするだけ、みたいな感じのモジュールであ ればそこそこ実用的に使えそうです File::Temp など 22 / 23

Slide 23

Slide 23 text

ご清聴ありがとうございました 23 / 23