こんにちは。コミュニケーションアプリ「LINE」のモバイルクライアントを開発している石川です。
この記事は、毎週木曜の定期連載 "Weekly Report" 共有の第 58 回です。 LINEヤフー社内には、高い開発生産性を維持するための Review Committee という活動があります。ここで集まった知見を、Weekly Report と称して毎週社内に共有しており、その一部を本ブログ上でも公開しています。(Weekly Report の詳細については、過去の記事一覧を参照してください)
言葉にできない
以下のような、ABC と XYZ という 2 種類のパラメータの文字列形式があるとします。
- ABC:
a=foo,b=bar,c=baz
- XYZ:
x:foo;y:bar;z:baz
これらの文字列からパラメータ (foo
, bar
, baz
) を取得したいです。そこで以下のように、正規表現を定義するオブジェクト Regexes
とパースを実行するオブジェクト Parser
を定義しました。(Kotlin の object
はシングルトンを作成するキーワードです。)
このコードには何か問題がありますか?
秘密を心に秘めておく
Parser
の関数 parseAbcParams
は、正規表現 ABC_REGEX
が名前付きグループ a, b, c を持つことを期待しています。同様に parseXyzParams
は、正規表現 XYZ_REGEX
内に x, y, z がこの順番で存在することを期待しています。
しかし、これら関数が正規表現に「期待」していることは明示的ではありません。正規表現を変更したときに、関数を更新するのを忘れてもコンパイルエラーにはならず、実行時にはじめてバグに気づく...という事態になりかねないでしょう。
この問題を軽減するためにも、「暗黙の期待」をする関係を、クラスや関数などのスコープに閉じ込める ことが望ましいです。今回の文字列からパラメータを取得する場合は、 Regexes
と Parser
でオブジェクトを分けるのではなく、ABC のパーサと XYZ のパーサでクラスを分けるとよいでしょう。
関数や値の間に暗黙の期待がある場合、それらを近く (同じ関数、クラス、モジュール内) に置くことで、その関係を明確にしやすいです。
公然の秘密
このような暗黙の関係はさまざまな状況で発生します。以下はその例です。
- 対となるロジック: シリアライズとデシリアライズ、await と notify
- 関数の呼び出し順序: setup -> execution -> teardown
- 文字列中のプレースホルダの数や種類: UI テキスト、SQL クエリ
- エッジケース除外済み/未除外の値: 0 を除外した
UInt
, 空文字列を除外したString
- 未処理/処理済みの文字列: 暗号化された文字列と平文、エスケープされた/されていない文字列
もちろん、関係を型で明示的に定義できる場合は、それに越したことはありません。暗号化されたテキストと平文を区別したい場合は、それぞれ EncryptedText
と PlainText
を定義するのが安全です。しかし、型を定義することが過剰になる場合や、ランタイムでないと検証できない場合もあります。
次善の策は、暗黙の関係を関数/クラス/モジュールに限定するか、ユニットテストやインテグレーションテストで保証する ことです。それも難しい場合は、暗黙の関係が存在することをドキュメンテーションコメントとして明記するとよいでしょう。
一言まとめ
暗黙の期待が必要な場合、その期待の関係を関数/クラス/モジュールに閉じ込める。
キーワード: implicit expectation
, dependency
, encapsulation