パッケージ設計の原則を本で学ぶ
はじめに
本記事は『アジャイルソフトウェア開発の奥義 第2版(ロバート・C・マーチン 著、瀬谷 啓介 訳)』を読んで、パッケージ設計の原則について学んだことをまとめたものです。
パッケージ設計の原則とは?
6つの原則のうち、「再利用・リリース等価の原則」・「全再利用の原則」・「閉鎖性共通の原則」はクラスをパッケージに分類する指針であり、「非循環依存関係の原則」・「安定依存の原則」・「安定度・抽象度等価の原則」はパッケージ同士の相互関係の指針である。
つまり、前の3原則はパッケージの凝集度に、後の3原則はパッケージの結合度に関連している。
再利用・リリース等価の原則
再利用の単位とリリースの単位は等価になるという原則。
リリースするパッケージ内のクラスはすべて再利用できるものか、すべて再利用できないもののどちらかにしなければならない。「パッケージ内のクラスAは他のアプリケーション開発で再利用できるけど、同パッケージ内のクラスBは自分の作ったアプリケーションとべたべたに結びついてるから再利用できません!」は許されない。それなら、再利用できないクラスBはリリースするパッケージから除くべきである。
また、再利用できるクラス同士であっても、ユーザーにとって役に立つものと全く役に立たないものが同じパッケージに入っていることは望ましくない。何かのライブラリをリリースするとして、ユーザーがそのライブラリを利用する主目的となり得るクラスやそれに関連するクラス以外の、何の関係もないクラスがライブラリに入っていても、ユーザーからすれば要らないクラスをロードさせられているだけで何のメリットもない。
全再利用の原則
パッケージに含まれるクラスは、すべて一緒に再利用される。つまり、パッケージに含まれるいずれかのクラスを再利用するということは、そのほかのクラスもすべて再利用することを意味する。 という原則。
再利用・リリース等価の原則の節でも述べたが、ユーザーにとって役に立つものと全く役に立たないものが同じパッケージに入っていることは望ましくない。同様に、役に立つものがばらばらのパッケージに入っていることも望ましくない。
パッケージは複数のクラスで構成されることがほとんどである。そして再利用の範囲はパッケージを単位とする。この場合、例えばパッケージの中のクラスに変更があると、ユーザーにとってはそのクラスは一切使わないものであったとしても、パッケージをアップデートしなければならない。また、依存しあうクラスが複数のパッケージに入っていれば、ユーザーは再利用のために対象のパッケージをすべてダウンロードしてこなければならない。パッケージ同士の依存関係もわかりにくく、使いづらいものになってしまうはずだ。
パッケージは、関連性の薄いクラスをまとめてはならず、関連性の強いクラスはまとめるべきである。
閉鎖性共通の原則
パッケージに含まれるクラスは、みな同じ種類の変更に対して閉じているべきである。パッケージに影響する変更はパッケージ内のすべてのクラスに影響を及ぼすが、他のパッケージには影響しない。 という原則。
SOLID原則の 単一責任の原則 と オープン・クローズドの原則(のクローズドの部分) の合体のような原則。
単一責任の原則では、クラスやメソッドが変更される理由は一つでなければならないということが示されているが、パッケージに関しても同様のことがいえる。
たとえば、一つの「切符販売」パッケージに料金計算クラスとディスプレイ描画クラスと切符印刷クラスが一緒くたに実装されていた場合、「計算のロジックが変わった」「ディスプレイの表示項目が増えた」「印刷のフォーマットが変わった」などの理由でそれぞれのクラスに変更が入る。このとき、他のクラスに何ら変更がなかったとしても「切符販売」パッケージは再リリースが必要になってしまう。また、例えば料金計算機能が複数のパッケージにまたがって実装されていた場合は、料金計算機能に変更があるたびに複数のパッケージを変更してテストしてリリースすることになる。
変更理由が同じクラスは一つのパッケージにまとめ、変更理由の異なるクラスはパッケージを分ける。これにより、仕様変更時の影響範囲を狭め、リリースの負担を軽減することができる。
非循環依存関係の原則
パッケージ依存グラフに循環を持ち込んではならないという原則。
パッケージAはパッケージBに依存し、パッケージBはパッケージCに依存し、パッケージCはパッケージAに依存し……という依存関係のことをパッケージの循環という。
パッケージに循環があると、循環を構成するパッケージの一つに依存したとしても、結果的に循環の他のパッケージすべてに依存しているのと同じことになってしまう。つまり、実質的に循環するパッケージと循環の要素に依存するパッケージは一つの大きなパッケージであるの同義である。
解消するには2つの方法が考えられる。
- 依存性逆転の原則に則り、依存する側のパッケージに依存される側のクラスのインタフェースを用意して依存の方向を逆転させる。
(例えばパッケージB内にパッケージCのインタフェースCを用意して、パッケージCがインタフェースCを利用することで依存の方向をパッケージC→パッケージBに変える) - 新しいパッケージを追加し、循環するパッケージのうち一つの依存について依存する側・依存される側の両方が利用しているクラスを新しいパッケージに移動することでパッケージ間の依存を切る。
(例えば新しくパッケージDを作り、パッケージBとパッケージCの双方が利用するクラスをパッケージDに移動することで、パッケージB→パッケージC間の依存を切る)
安定依存の法則
安定する方に依存せよという原則。
「このパッケージは今後変わる可能性がある」と、変更することを意識して作ったパッケージに、変更の難しいパッケージが依存することはあってはならない。結果的には変更することを意識して作ったパッケージの変更が難しくなるか、変更の難しいパッケージを頑張って変更することになる。
もちろん、パッケージは全て安定していなければならないわけではない。システムの上位レベルの設計(→設計上の変わってほしくない部分)などの安定していてほしい設計と、変わりやすい下位の設計は別のパッケージに分けるなど、安定した設計と不安定な設計を同じパッケージに入れないことも大切である。
安定度・抽象度等価の原則
パッケージの抽象度と安定度は同程度でなければならないという原則。
安定度の高いパッケージは抽象的でなければならない。
安定依存の法則にも関連するが、システムの上位レベルの設計(→設計上の変わってほしくない部分)を安定したパッケージに入れると、設計が変えづらくなる。
そのため、安定したパッケージに含まれるクラスは修正を加えずに拡張できる柔軟なものでなければならない。
安定依存の法則と安定度・抽象度等価の原則を合わせると、依存の方向は抽象度の低い(→変わりやすい)パッケージから、より抽象度の高い(→安定した)パッケージに向かう向きにすべきということになる。つまりは抽象度の低いクラスのインタフェースを作って抽象度の高いパッケージに置き、抽象度の低い側から依存させるという、依存性逆転の原則のパッケージ版になる。
まとめ
パッケージ設計の原則と銘打ちつつも、SOLID原則と類似した考え方をする部分が多かった。
個人的は、安定依存の法則と安定度・抽象度等価の原則について学ぶ中でSOLIDの中でも難解(※個人的主観)な依存性逆転の原則の考え方が腑に落ちた部分もあった。