技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

「共通化の罠」を見た感想

数日前にTwitterで「共通化の罠」という動画がバズりました。

https://togetter.com/li/1354658

 
内容はリンク先の動画を見ての通りなのですが、一言でまとめると「複数のモジュールで同じロジックを使用していたので、そのロジックを共通モジュールとして外に切り出した。しかし、共通モジュールに対して、個別の呼び出し元モジュールに特化した処理が保守対応で次々と入り込むことで、各々のモジュールが密に結びついてしまった(各モジュールを別々のシステムで動かすことが困難になった)。」という話です。
 
恐らく、上記で言う「同じロジック」というのは、「似ているビジネスロジック、ある時点では同じだったビジネスロジック」と書いた方が正確でしょう。
「似ているけど、それぞれの業務でロジックが微妙に違う。変化も多い。」というのはビジネスロジックあるあるなので、ビジネスロジックは安易に共通化せず、システム基盤となるような機能を積極的に共通化するのが良い設計方針だと思います。
 
しかし、似たようなビジネスロジックが点在しているのもそれはそれで問題(改修時に影響見落としやテスト工数増を招く)なので、ビジネスロジックも何らかの方法で共通化する必要はあります。
ビジネスロジックを共通化する設計方針としては、「これ以上分割できないという単位で機能毎に部品化・共通化する」というのが良い設計方針だと思っています(モジュールにするには1単位1単位が小さすぎるのであれば、共通関数でも良いです)。その共通モジュール(共通関数)を組み合わせればそれぞれの業務の処理ができる、という状態にするのが理想です。
 
例えば、RPGで魔法を使った時の処理を実装する際、ダメージ計算式は共通でも、魔法の種類毎に「命中判定の有無」「ダメージの振れ幅(乱数化)の有無」といった細かい違いがあったりします。そのような場合は、「ダメージ計算」「命中判定」「ダメージ乱数化」といった単位で共通モジュール(共通関数)を作成し、「魔法Aは命中判定有で乱数化無だから「ダメージ計算」「命中判定」を呼び出す」「魔法Bは命中判定有で乱数化有だから「ダメージ計算」「命中判定」「ダメージ乱数化」を呼び出す」といった形でそれぞれの魔法の処理が可能になります。
 
保守対応で業務毎に新たな差異が発生するようになる、というのは良くある話です。その場合は、新たな共通モジュール(共通関数)を作成し対応します。
RPGの例で言うと、新作で魔法Aと魔法Bで別々の命中判定式になった場合は、「命中判定」の共通モジュール(共通関数)を「命中判定A」「命中判定B」の二つに分け、「魔法Aは「命中判定A」、魔法Bは「命中判定B」を使用する」という形にして対応します。
 
「これ以上分割できないという単位で機能毎に部品化・共通化する」という方針であれば、「各モジュールを別々のシステムで動かす」なんて話が出てきても容易に対応できます。各モジュール+各モジュールで呼び出している共通モジュールのみを別システムに持ち出せば良いだけになります(RPGの例で言うと、魔法Aなら「ダメージ計算」「命中判定A」だけ持ち出せば良い)。密に結びついたモジュールを大量にソース修正して無理矢理引きはがしたり(移植で大量の工数が必要になったり)、移行先システムでは使用しない機能まで丸ごと持ち出したり(移植先システムの保守性が悪化したり)することは無くなります。
 
情報処理技術者試験で出題される「モジュール強度」に似たような話が出てくるので、もう一度おさらいしても良いかもしれません。動画の例は、フラグ制御(どのモジュールから呼び出しているのかの情報を受け渡し)で結合する「論理的強度」のレベルになってしまった例である、と考えています。