リフレクションはなぜ使ってはいけないのか
言語や用法によります。使っていいところでは使っていいです。今回はC#の話に限定します。もっといえばアプリケーションコードを想定。
よく言われる実行速度は大きな問題にはなりません。もちろんユースケースによりますが、計算機性能が上がった今では実行速度はそこまでシビアな要求にはなりません。 例えばWebでは他の通信の方がロスが大きいです。
真に問題なのは静的解析が効かなくなることです。呼び出されるメソッドが間違っていようがコンパイル時にエラーが出なくなってしまいます。
加えてエディタの機能にも問題が出ます。C#だとVisual Studioを使っていることが多いですが、 例えばメソッドがどこからどれだけ使われているか調べることができます。リフレクションだとこれがわかりません。 例えばメソッドの名前を変更すると参照している場所の名前も全て変更できます。リフレクションでは変更が付いてきません。
エディタはソースそのものではありません。が、開発を便利にするエディタを使用してソースを書いていれば、その機能があること前提のソースになります。機能を失くせば必ず変更時に見落としが発生します。
この問題に対処する方法は、テストコードを書いて動作を保証することです。これならリフレクションが使われていても問題はありません。
C#は型があるのでテストコードを書かなくても割と何とかなります。本当に。書いてない現場だって多数あるでしょう。 ですがリフレクションが出てくれば話が違います。 リフレクションを書くような需要が出てくれば一緒にテストコードを書くことを意識しなければなりません。
ちなみにC#ならReflectionTypeLoadExceptionとか気を付けたいです。わかっていれば踏みませんがリフレクションはわかっている率があまり高くないと感じます。
こんなValidatorは許されない
PasswordValidator.IsValid(passwordString);
ある日、こんなコードを見つけました。引数の型はstring。PasswordValidatorで正当性をチェックしているというのは一目見て明らかです。
では、これは具体的に何をチェックしているんでしょう?
- 文字列の長さ?
- パスワードに使える文字の種類?
- 大文字小文字に記号や数字を要件に従って含んでいること?
- 過去に設定されたパスワードとの重複していないこと?
このコードだけを見ると、サービスのパスワード要件を満たしているかどうかを判定してくれることを期待します。上の箇条書きなら全部。
しかし残念なことに、これは文字列の長さしか見ていませんでした。何がValidだというのか。
幸いなことにこれのすぐ下に文字種などのチェックがそのまま書いてあったので、モジュールの中まで見なくてもこれが役立たずだということがわかったわけですが……。
自分で書き直すならどうするかというと。
オブジェクト指向設計的に、パスワードオブジェクトの生成メソッドを作ってバリデーションをその中で持たせます。ただし、長さや文字の種類を見るくらいです。過去のパスワードとの重複チェックは別に書きます。そうしないと重複したパスワードがコンテキストの中で生成できませんからね。
パスワード要件は将来的に変化する可能性があるので、パスワードオブジェクト自体は文字列を何でも持てるようにします。最低限中身がイミュータブルでnullじゃないことを保証するところ。