プログラムも規模が大きくなると管理が大変です。クラスや関数をある程度のまとまりとして分けたほうが、管理しやすくなります。このまとまりがパッケージです。
共通的なクラスをまとめてパッケージにする、程度はなんとなく分かります。
実際にはどのような指針に従うべきなのでしょうか。
パッケージ設計の考え方
パッケージ設計とはつまり、どのクラスをまとめ、どのクラスを分離するか、を考えることです。
ポイントは3つあります。
- 関連性 関連性の強いクラスをまとめる
- 依存性 依存方向が同じクラスをまとめる
- 抽象度 抽象度が同程度のクラスをまとめる
うまくまとめられたパッケージは、あとで再利用がしやすくなります。
再利用・リリース等価の原則
REP:Reuse-Release Equivalency Principle
再利用の単位とリリースの単位は等価になる。
パッケージは段階的にバージョンアップします。Ver1.0、1.1、2.0…のように。理由は、パッケージ内クラスの不具合修正、新たな機能の追加、などあるでしょう。
パッケージとしてリリースするときは、そのパッケージのクラス全てが利用可能でないといけません。中途半端な状態でリリースしてはいけない、ということです。
あるパッケージに、クラスAとクラスBがあるとします。
パッケージVer1.0に不具合があり、関連するクラスAとクラスBを修正した、としましょう。
このとき、クラスAを修正してVer1.1リリース、クラスBを修正してVer1.2リリース、というのはNG。
AとBを両方修正した完璧なバージョンでリリースすべきです。
パッケージをバージョン管理システムで管理しているなら、問題は起こりにくいでしょう。
共通再利用の原則
CRP:Common Reuse Principle
パッケージに含まれるクラスは、すべて一緒に再利用される。
パッケージを使うとき、たいていは1つのクラスだけ使うわけではありません。複数のクラスが関連します。例えばメソッドの引数、戻り値、に別のクラスを使ったりします。
こういったクラス群は、一緒に使われる傾向にあります。互いに強い関連性を持つクラスは同じパッケージに含めるのがよいでしょう。
クラスの一部だけパッケージの外に出したりすると、パッケージの整合性が取りにくくなります。
閉鎖性共通の原則
CCP:Common Closure Principle
パッケージに含まれるクラスは、みな同じ種類の変更に対して閉じているべき。
ソフトウェアの修正は必ず起こります。パッケージ内のクラスも同様です。
でも修正がパッケージで完結すれば、他には影響を与えません。いっしょに修正しそうなクラスなら、同じパッケージに入れたほうがいいでしょう。
逆に言えば、異なる理由で修正されるクラスは、同じパッケージに入れるべきではないということです。これはパッケージ設計における単一責任原則です。
よくあるのが、共通的なクラスをなんでも詰め合わせたCommonパッケージです。すべてのクラスがCommonパッケージを参照してしまい、影響範囲がはたしてどこまで及ぶか分からなくなります。
パッケージを考えるとき、実際には「再利用」より「保守性」を重視します。保守時の修正やテストの影響度から、パッケージの変更理由は一つであるべきなのです。
非循環依存関係の原則
ADP:Acyclic Dependencies Principle
パッケージ依存グラフに循環を持ち込んではならない。
循環依存とは、パッケージAを使うにはパッケージBが必要、パッケージBを使うにはパッケージAが必要、と依存関係がぐるぐる回ってしまうことです。
クラス単体なら分かりやすいのです。しかしパッケージになると、めぐりめぐって循環している場合があり、気づきにくいのです。
こうなると、パッケージAとBは独立して使えず、実質ひとつの大きなパッケージになってしまいます。
パッケージ設計をするときには、必ず片方向の依存関係となるようにしましょう。
安定依存の原則
SDP:Stable Dependencies Principle
安定する方向に依存せよ。
パッケージAとパッケージBに、依存関係があり、
- パッケージA:安定(変更されにくい)
- パッケージB:不安定(変更されやすい)
という場合、不安定なBパッケージが、安定しているAパッケージに依存されてはいけません。
「安定している」とは、変更しづらいという意味。変更しづらいというのは「他のクラスやパッケージから依存されている」(=責任を負っている)パッケージです。
パッケージの依存関係は、より安定しているパッケージへと向かうべきです。
安定度・抽象度等価の原則
SAP:Stable Abstractions Principle
パッケージの抽象度と安定度は同程度でなければならない。
基本的に、
- 安定度の高いパッケージは、抽象度が高い
- 不安定なパッケージは、具体的である
という関係があります。安定したパッケージに出来るだけ変更を発生させないためには、それは抽象的である必要があります。
依存関係は安定する方向へ向かうべき。そして、安定するということは抽象化されているということを意味します。
いろいろなところから参照されているパッケージが、抽象度合いが低い場合、いろいろな変更が多くなってしまい、安定度の低いシステムとなってしまう。安定したパッケージは抽象度を高く、不安定なパッケージは具体的にします。
パッケージの方針として、
- 安定したパッケージは抽象クラス中心で構成する。柔軟さや設計の自由さを残す。
- 不安定なパッケージは具象クラス中心で構成する。具体的なコードを変更可能とする。
安定依存原則とこの原則は、パッケージにおける依存関係逆転原則といえます。
まとめ
パッケージ設計を考えだすということは、プログラムもある程度大規模になっているところでしょう。
パッケージ構成を考えるにあたり、世の中にあるライブラリやフレームワークをみてみるのもよいでしょう。各々コンセプトは違いますが、永年の英知が詰まったパッケージ構成となっていて参考になります。
パッケージ分割を誤ると、保守性に重大な支障が発生します。慎重に考えましょう。
コメント