Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Perlにおけるクラスの実装パターン.pdf

36e748c47de34549cfa3ac0950c5351d?s=47 ybrliiu
November 22, 2018

 Perlにおけるクラスの実装パターン.pdf

36e748c47de34549cfa3ac0950c5351d?s=128

ybrliiu

November 22, 2018
Tweet

Transcript

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

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

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

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

    4 / 23
  5. ハッシュベースのクラス ハッシュリファレンスにパッケージ名を紐付かせる インスタンス変数はハッシュリファレンスの要素に格納 最も一般的なクラスの実現方法で、大体のクラスビルダーは これがベース package Point; sub new {

    my ($class, $x, $y) = @_; bless +{ x => $x, y => $y }, $class; } sub x { my $self = shift; $self->{x}; # インスタンス変数 } 5 / 23
  6. メリット 実装が簡単 理解しやすい 拡張性がある デバッグが容易 比較的速度が速い 6 / 23

  7. デメリット 比較的メモリ使用量が多い カプセル化が不十分 メソッドを介さずとも容易にインスタンスの状態を変更 することができる 自由に値を追加 / 削除 / 変更できる構造体をプログラ

    ムの中で引きづり回せると考えると、厳しい物がある my $point = Point->new(-3, 5); $point->{x} = 100; # accessorを介さなくてもインスタンス変数を操作できてしまう say $point->x; # -> 100 $point->{name} = 'Anonymous'; # こんなこともできてしまうわけです 7 / 23
  8. ハッシュ以外のデータ構造のリファレンスでもクラスは実現できま す!!! 8 / 23

  9. 配列ベースのクラス 配列ファレンスにパッケージ名を紐付かせる インスタンス変数は配列リファレンスの要素 大昔からあるモジュールでよく使われている 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
  10. メリット メモリ使用量が少ない インスタンス変数へのアクセス速度が速い 10 / 23

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

    / 23
  12. サブルーチンベースのクラス サブルーチンリファレンスにパッケージ名を紐付かせる サブルーチンリファレンスを実行することでインスタンス変数 を取得する 昔の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
  13. メリット きちんとカプセル化されている 実装しているクラスだけが知っているはずのインスタン ス変数の取り出し方法がわからない限り 13 / 23

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

  15. スカラベースのクラス スカラリファレンスにパッケージ名を紐付かせる インスタンス変数は紐付けたスカラリファレンスの実体のみ スカラ値をラップするだけのようなモジュールや、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
  16. メリット メモリ使用量が非常に少ない 16 / 23

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

  18. Insideout Object スカラリファレンスベースのクラスを応用したもの インスタンスのアドレスを数値として評価し、それをキーとして package localなハッシュ変数などにインスタンス変数を格納 メモリを開放する処理を自前で実装しなければならない インスタンスが必要なくなってもGCがメモリを開放する のはインスタンスの領域だけで、 各インスタンス変数の

    データはpackage localな変数に残り続ける Perl Best Practiceで推奨されている方法だが、あまり普及し なかった 18 / 23
  19. 実装 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
  20. メリット インスタンス変数を完全にクラスの外部から隠蔽している 拡張性がある カプセル化を実現しているクラスの実装パターンのなかでは 速度が最も速い 20 / 23

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

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

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