Slide 1

Slide 1 text

Perlにおける動的なモジュールロードの メリットとデメリット YAPC::Nagoya::Tiny @_ybrliiu

Slide 2

Slide 2 text

自己紹介 ● id: mp0liiu / @_ybrliiu ● 所属 : 株式会社モバイルファクトリー ● 新卒2年目(23歳) ● Perl歴約7年 ● 最近はソフトウェアアーキテクチャとか型に関心があります ○ https://github.com/ybrliiu/p5-Types-TypedCodeRef

Slide 3

Slide 3 text

Perlの好きなところ ● TMTOWTDI ● 特にオブジェクト指向の実現方法がいろいろあるところ ○ Any reference base class, Inside-out class, いろんなクラスビ ルダー, etc... ● 最近Perlコアにオブジェクト指向の構文を入れようという提案がでて いたりしていてテンションが上っています

Slide 4

Slide 4 text

よろしくお願いいたします!!!

Slide 5

Slide 5 text

なぜこの発表をしようと思ったのか ● これまで趣味 + お仕事で動的なモジュールロードを行うコードをカ ジュアルに書いてきました ● そのようなコードを運用していると、後から辛くなってくることが多かっ たので、なぜそうなっているのかを整理し、どうすれば改善できるのか を考えました ● http://mp0liiu.hatenablog.com/entry/2019/02/28/111424

Slide 6

Slide 6 text

次のようなコードがありました ● とあるソーシャルゲームのコード ● アイテムを使用すると、アイテムの種類に応じて効果が発動する ● アイテムのデータはDBで管理されている

Slide 7

Slide 7 text

エンジニアA 「いちいちアイテム効果クラスを1つ1つuseしてアイテム の種類と対応させるコードを書くのめんどくさいなー」 エンジニアA 「せや!アイテムデータの種類を見て正規表現でいい感 じのアイテムクラス名を作ってそれをロードするようにするんや!」 エンジニアA 「よし!簡潔に書けていいな!」

Slide 8

Slide 8 text

数カ月後・・・

Slide 9

Slide 9 text

新しい効果をもつアイテムを追加することになりました

Slide 10

Slide 10 text

エンジニアB 「新しいアイテムのデータも追加したしアイテムの効果も 実装するか」 エンジニアB 「とりあえず他のアイテムのクラスを真似て作ろう」 エンジニアB 「クラス名はなんてすればいいんだ? grepしてもアイテム 効果クラスが使われている場所が見つからないぞ?」

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

今回の発表で伝えたいこと ● 動的なモジュールロードは柔軟性がありすぎるので下手な使い方をす ると保守性が下がってしまう ○ 静的なモジュールロードを行う方法でできないか検討しよう ○ 動的なモジュールロードをする方が合理的なら保守性が下がらな いように工夫しよう

Slide 13

Slide 13 text

今日話すこと ● 静的なモジュールロードと動的なモジュールロードについて ● 動的なモジュールロードのメリット ● 動的なモジュールロードのデメリット ● 保守性を下げないようにするには

Slide 14

Slide 14 text

静的なモジュールロード ● コンパイルフェーズで行うモジュールロードのこと ● 通常は use でロード ● ロードするタイミングやロードするモジュールに制約がかかる ○ 少なくともランタイムが開始する前にどんなモジュールがロードさ れるかが定まるようになっていないけない

Slide 15

Slide 15 text

動的なモジュールロード ● ランタイムで行うモジュールロードのこと ● ロードするタイミングやモジュールを好きなように決められる ● 言語組み込みの機能で行うのなら require でのモジュールロード ○ ロードするモジュールを動的に決めたいなら eval “require ...” ● 通常は Module::Load, Class::Load などといったモジュールローダー を利用してロード

Slide 16

Slide 16 text

動的なモジュールロード ● use する場合と挙動が違うところがあるので注意 ○ import は自動的に呼ばれない ○ CHECK, INITブロックに書かれたコードが実行されない

Slide 17

Slide 17 text

動的なモジュールロードを行うことによる メリット あるいはどのようなときに動的なモジュールロードを行いたくなるのか

Slide 18

Slide 18 text

コード量が少なくなる

Slide 19

Slide 19 text

for, while 文などでまとめてロードすればコード量が減る

Slide 20

Slide 20 text

Module::Find を利用すると、ある名前空間の下に属するモジュール をすべて読み込むことができる

Slide 21

Slide 21 text

Catalyst 風のモジュールローダーを利用する ● useしなくてもモジュールを使える、use忘れがなくなる ● パッケージ名を短く書ける

Slide 22

Slide 22 text

プラガブルなモジュールを簡単に作れる

Slide 23

Slide 23 text

モジュールに新たなメソッドを追加したりやモンキーパッチを当てれる ようにしたい場合 (Tengのようなケース)

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

与えられたものを読み取って何かするようなモジュールに、ルールを 追加していける構造にしたい (Perl::Critic のようなケース)

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

動的にディスパッチできる ● 利用するモジュールを動的にディスパッチできる ● プログラムの外部で管理されていたり、外部から受け付ける入力に応 じて処理を分岐させたい場合に非常に便利 ○ 項目が多いマスタデータの種類に応じて ○ HTTPリクエストの内容に応じて

Slide 28

Slide 28 text

動的にディスパッチできる

Slide 29

Slide 29 text

起動 / ロードにかかる時間の短縮 ● モジュールをロードするタイミングをモジュール内のコードを実行する 直前にまで遅らせることで、アプリケーションの起動やモジュールの ロードにかかる時間を短くすることができる ○ 当然その分実行時に短くした分の時間がかかる ● 高速化を意識しているCPANモジュールや巨大なアプリケーションで よく見かける ○ Moo, Type::Tiny, Class::Accessor::Lite, etc...

Slide 30

Slide 30 text

依存関係を動的に解決できる ● 依存関係を動的に解決できる ○ 条件に応じて依存モジュールを変更できる ○ モジュールが相互に依存しても警告が発生しない ● モジュールロードに失敗した場合の処理が書ける

Slide 31

Slide 31 text

動的なモジュールロードを行うことによる デメリット 開発体験にどのような悪影響がでるのか

Slide 32

Slide 32 text

可読性が下がる ● 動的にロードするモジュール名を組み立てていると発生する問題 ● 特に動的にモジュールロードされていることを知らない人がコードを 読むと、どこで何のモジュールが使われているのかがわかりにくい ○ 特に正規表現でモジュール名を作ったりしていると非常に厳しい

Slide 33

Slide 33 text

可読性が下がる ● 本質的には greppability の話 ○ 調べたいコードが検索しにくい状態になってしまう ○ 動的にシンボルを組み立てているとコードが検索しにくくて機械 も人間も辛い思いをする ○ 静的解析を利用したツールの恩恵を授かれない恐れも

Slide 34

Slide 34 text

良くない設計の原因になる ● どこでモジュールがロードがされるかわかりにくくなるので、注意しな いと混沌とした依存関係や構造ができあがる ● モジュールの先頭で依存しているモジュールがまとめて use されて いる方が依存関係はわかりやすい ● 依存関係が循環しているモジュールがあっても警告がでない

Slide 35

Slide 35 text

実際にロードされるまで動くかわからない ● 依存しているモジュールがエラーなどで動かなくなっていても、実際 にコードが動くまでわからない ● しっかりテストが書かれていたり、 Test::UseAllModules::all_use_ok みたいなテストが用意されてい るならそんなに問題にはならない ● またリッチな機能をもつエディタなら Syntax Error とかにも気づきや すい

Slide 36

Slide 36 text

つまり、むやみに動的なモジュールロード を行うと保守性が下がる

Slide 37

Slide 37 text

静的にモジュールロードする場合、柔軟性がない分 保守性が下がるような書き方をしにくい

Slide 38

Slide 38 text

なるべく静的にモジュールロードしたい

Slide 39

Slide 39 text

動的なモジュールロードを行うほうが合理的なときは、 なるべく保守性が下がらないようにしたい

Slide 40

Slide 40 text

どうするか

Slide 41

Slide 41 text

コード量を減らしたいとき

Slide 42

Slide 42 text

パッケージ名が長すぎる モジュールローダーの代わりに 定数や aliased でパッケージ名のエイリ アスを作る

Slide 43

Slide 43 text

use 忘れを防ぎたい ● 現状代替案は知りません・・・ ● いい方法を知っている方がいれば教えて下さい! ● 理想を言えば他の言語みたいにエディタやIDEの機能でパッケージを 利用していたら自動でuseできるようになってほしい ● うたがわさんが便利そうなプラグインを作られていました ○ 開発体験良くなりそうですね

Slide 44

Slide 44 text

まとめてロードしたい ● 普通にuseする方法では不可能 ● そもそもたくさんのモジュールをロードしたいケースは頻繁にあるの か? ○ 拡張性を重視する構造でなければ、責務が大きくなりすぎている 可能性がある ○ その場合はモジュールを分割すべき

Slide 45

Slide 45 text

まとめてロードしたい ● 拡張性を重視していたり、useを延々と書き続けるのが現実的ではな い場合は動的にロードすべき ● ただし保守性が低下しないように工夫する ○ まとめすぎない (Module::Find::useall()) ○ 動的にパッケージ名を組み立てない ○ Test::UseAllModules などを利用してコンパイルフェーズでエ ラーが起きないかをテストするようにする

Slide 46

Slide 46 text

プラグイン機構を実装したい時 ● 手間がかかるが、プラグインオブジェクトを外でuseして作って渡すこ ともできる ● フレームワークだったりプラガブルにしたい場合は動的にロードする のもよい ● プロダクトのコード、特にビジネスロジックなどではプラガブルな構造 を用意するのはやり過ぎなケースが多い ○ 基本的に保守性を重視して静的にロードする

Slide 47

Slide 47 text

モジュールに新たなメソッドを追加したりやモンキーパッチを当てれ るようにしたい場合 (Tengのようなケース)

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

動的にディスパッチしたい時 ● 利用しうるモジュールは全て事前にロードしておき、外部からの値に 応じて利用するモジュールを分けるような対応表を書くことで対応で きる ○ 数が多いなら対応表を自動生成する仕組みをつくるという手も ● 工数、拡張性と保守性のトレードオフになるので状況に応じて判断 ● 利用するパッケージ名を難しい正規表現とかで作っている場合は読 みにくくなるので対応表をちゃんと書くべき

Slide 50

Slide 50 text

動的にディスパッチしたい時

Slide 51

Slide 51 text

起動 / ロードにかかる時間の短縮 ● 起動やロードにかかる時間を短縮したい場合は動的にロードするし かない ● 保守性との引き換えになることを念頭に置いた上で、なるべく保守性 がおちないように工夫する ○ 良くない設計になってしまわないように注意を払う ○ 動的にパッケージ名を組み立てない ○ Test::UseAllModules などを利用してテストする

Slide 52

Slide 52 text

依存関係を動的に解決したい ● 条件が実行する前に決まるなら if プラグマを利用したり、importや BEGIN句の中で頑張って静的にロードさせるようにすることができる ● 依存関係が循環しているような場合は結合度が高く、設計としてよく ない状態になっているので設計を修正する

Slide 53

Slide 53 text

まとめ

Slide 54

Slide 54 text

まとめ ● 動的なモジュールロードは下手な使い方をすると保守性が下がってし まう ○ 静的なモジュールロードを行う方法でできないか検討しよう ○ 動的なモジュールロードをする方が合理的なら保守性が下がらな いように工夫しよう

Slide 55

Slide 55 text

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