「ちょうぜつソフトウェア設計入門」第2章 パッケージ原則 まとめと感想

ソフトウェア設計

本書について

ちょうぜつソフトウェア設計入門 PHPで理解するオブジェクト指向の活用

技術書「ちょうぜつソフトウェア設計入門 PHPで理解するオブジェクト指向の活用」レビューとまとめ。Bobおじさん本を読む前の準備として最適な一冊。からリンク。

第2章 パッケージ原則

2-1 再利用・リリース等価の原則
2-2 全再利用の原則
2-3 閉鎖性共通の原則
2-4 非循環依存関係の原則
2-5 安定依存の原則
2-6 安定度・抽象度等価の原則
2-7 アーキテクチャの外側

本章では、複数のコードをどうやってまとめて再利用すれば良いのかがわかる。パッケージ原則は、アーキテクチャ設計には欠かせない重要なルール。ソフトウェアの全体像となるため、個々のプログラミングテクニックよりは高度なトピックとなる。しかし、全体を見ずに小手先の技を使うのでは、ただの好みの問題になってしまう。ここで学べる原則は、オブジェクト指向の基礎となる。これら原則を守ることで、凝集度を高くし、疎結合につながる。

実は、Robert C. MartinのClean Architectureを以前読んだ時は、何となくしかわかっていなかった。何となくそうだよな〜と読んでいたが、自分の中で消化しきれていなかった。しかし、この本は非常にわかりやすく、その重要性を理解することができた。他書で、ここまでこのパッケージングについてわかりやすく説明した本はほぼ無いのではと思っている。ぜひ、購入して読むことをお勧めする。

再利用・リリース等価の原則

Reuse-Release Equivalence Principle (REP)

リリースされたものだけを再利用しなさい。再利用させたければリリースしなさい。という、もっとも重要な大原則。

自分のプロジェクトに何らかのライブラリが必要となったとき、わざわざ開発中のバージョンを使いたいとは思わない。バージョン番号付きで公開されたものでなければ安心できない。ほしい機能が実装されていても、他の未実装機能との兼ね合いで仕様変更になるかもしれないし、バグが潜んでいて、後で発覚するかもしれない。公開されているライブラリだけではなく、アーキテクチャのレイヤー分け、レイヤー内での部品分け、単にフォルダ分けしただけのコード、といった複数のコードの集まりをパッケージと考える。PythonやNode.jsではファイルでさえパッケージとなる。チーム開発では、git branchのmain/masterブランチにマージされるタイミングが、開発チームメンバーへのリリースと言える。

別の人が作り終わるのを待ってたら間に合わない。並行で開発する必要がある。だからこそ、きちんとリリース待ちができるよううまい部品分けが必要となる。うまく分けられていないと、自分が利用しているコードが書き換えられたり、その逆が起きたりして、開発者は必要な変更を躊躇してしまい、全体の作業の遅れにつながる。再利用の単位を適切に分け、OSSのようにリリースと再利用が一致するまとまりの粒度を設計するべき。

どうやって、適切な再利用単位を見極めたらいいか、のヒントが次の全再利用の原則(CRP)と閉鎖性共通の原則(CCP)にある。

全再利用の原則 「責務に対して最小に」

Common Reuse Principle (CRP)

再利用・リリース等価の原則 (REP)を前提にすると、ひとつのパッケージ(またはバージョン)に起きたコード変更は、すべて利用するか、まったく利用しないかのどちらかを選ぶしか無い。全再利用の原則(CRP)は、パッケージの利用者に「この変更は必要だがこの変更は採用したく無い」といった取捨選択の権限がないことを改めて強調している。

外部の依存ライブラリのバージョンアップのとき、欲しい改善と都合の悪い下位互換切りが両方入っている経験をしたことがある人は、多いのではないでしょうか

これは正に経験したことがある。多くのソフトウェアエンジニアが経験していると思う。

これは、ひとつのソフトウェアの異なるパッケージ間でも起きる。

一見、全再利用の原則(CRP)は、利用者に課せられた制約のように見えるが、実際はパッケージングをする側が理解しておくべき原則。つまり、利用者にそういった不便を強いる必要がないように、パッケージデザインを考える時はひとつのパッケージに多くを含みすぎてはいけないという注意喚起。機能が多くなければ、非互換性と機能改善で迷うことも少なくなる。きちんと分けれていれば、必要なパッケージだけ交換できる。交換部品の影響範囲が狭ければ、他の部分への影響の心配もかなり減る。

パッケージの責務/責任を重視する。パッケージの責務はひとつ、何かを完全にうまくやるが、それ以外の仕事をしないようにするのが理想。分けすぎると部品が多くなって理解しにくくなるのではと感じる人もいるかもしれないが、実際は逆で、開発が進むと小さくわけてある方が問題を理解しやすくなってくる。結局、細かくわけていようがコード量は同じ。であれば、ごちゃまぜよりも小さな部品同志の関係に着目できる方が遥かに楽。

最初はちょっと分けすぎぐらいで十分です。後で分けてあるものを混ぜるのは簡単ですが、混ざっているものを分け直すのは非常に困難です。一度でも混ざってしまうと、どこがつながっているかわからなくなりますから。

こうやって、実務でどうすれば良いかのアドバイスが散りばめられているのが良い。
この本では、パソコンとキーボードを使って、この概念をわかりやすく説明してくれていた。うまく説明するなと思った。ここはぜひ本を読んでほしい。
ちなみに、著者の方のTwitterを見ていたら、この概念に近いツイートを見つけた。

きれいに機能削除するのがどれぐらい難しいか #ちょうぜつエンジニアめもりーちゃん pic.twitter.com/GHJM26GTnC

— 田中ひさてる 新刊発売中 (@tanakahisateru) January 6, 2023

閉鎖性共通の原則 「責務に必要なものを含んで閉じよ」

Common Closure Principle (CCP)

全再利用の原則(CRP)が分けるべきものを分けよという教えだったのに対し、閉鎖性共通の原則(CCP)は、ひとつの変更が必要な時、できる限り一つのパッケージだけを交換すれば済む形にせよという教え。

全再利用の原則に従って、わけられる部品を細かく分けることだけを推し進めると、ひとつのフォルダに1ファイルしか入っていない箇所が大量に出てきてしまう。そうしてまとまっていないのは、「パッケージ」の意味がない。そうなると、あるパッケージの変更がすぐに別のパッケージへの変更を誘発し、まとまりとして不便かつ不自然となり、まとめている意味がなくなる。

良いパッケージには共通して、自身の責務セットがほどよいサイズで自己完結している特性がある。この完結性はよく「閉じている」と表現される。

閉鎖性共通の原則(CCP)を守ったパッケージは、利用者にとって扱いやすい。準じていないと、あちこち連鎖的に変更作業が必要になり、工数がかかってしまう。しかし、この現象は極めて頻繁に発生する。たった一つの機能変更が、各所に分散したフォルダに変更を強いてしまうことはよくある。さらに、コード変更が起きなくても、挙動が変わる可能性がある。少しでも変更が加わったファイルを含む機能部品は、その全体が以前と全く同じであると言い切れなくなる。ひとつの機能修正によってコード変更が起きるパッケージが閉じていれば、そのリスクはそのパッケージに直接依存しているパッケージのみになる

閉鎖性共通の原則(CCP)に加えて、全再利用の原則(CRP)によってパッケージの規模が最小化されていれば、変更リスクは非常に小さく閉じてくれます。扱いやすいパッケージは、何を含んで何を含まないかが上手くデザインされているおかげで、心配事をとてもシンプルな問題にしてくれます。シンプルさは開発者の安全につながります。開発者が安心できると、おのずと高い生産性につながります。

言うは易し行うは難しだが、こうしたパッケージ原則を守ることで、生産性の高さにつながる。こうやって、単に抽象的な説明だけではなく、こういうことをすれば、こんないいことがありますよ、と上手く説明してくれている。

ここまでの3原則は、ひとつのパッケージの凝集度に着目する原則。次からは、複数パッケージの関係に関する原則。

非循環依存関係の原則

Acyclic Dependencies Principle (ADP)

パッケージの依存が循環してはならない

循環依存するパッケージは分離できなくなる。使う時は常にセットで、使わなくなるのは全てが一斉に不要となるとき。相互に依存し合ったパッケージはセットでひとつの大きなかたまりになる。結局そうなると、今までの原則も破ることになってしまう。

循環依存は、パッケージ間では問題になるが、同一パッケージ内であれば問題はない

循環依存を防いで複数のパッケージの依存の向きを単方向に整えるには、何に気をつければ良いか。その指針が次の原則となる。

安定依存の原則

Stable Dependencies Principle (SDP)

パッケージの依存は常により安定したパッケージに向くという真理を語っている原則。「向けるべき」ではなく、「常に向く」とのこと。
ここでの安定は、「変更の起きにくさ」。依存パッケージに何らかの変更があると、それを使う側のパッケージは以前と同じことを保証できなくなる。
そのため、パッケージが依存してよいのは、より安定度の高いパッケージということになる。

クリーンアーキテクチャは、依存が常に円の中心に向かう。円の外側に行くほど、コードはアプリケーションの本質とは異なる外的な影響要因にさらされていき、不安定となる。

安定度・抽象度等価の原則

Stable Abstractions Principle (SAP)

安定度が高いパッケージであるためには、抽象度が高くなければならず、安定度の低いパッケージで良い場合は抽象度が低くても良い。

抽象度の高さは、次の4点にどれだけ近いかで決まる。

  • 抽象クラスやインタフェースなど実装詳細を自身から排除したもの
  • 上記のような詳細を持たないものだけに依存するロジック
  • 固有の業務にも特定技術にも関係しない時刻や配列などの汎用概念とその操作
  • プログラミング言語そのものや言語標準ライブラリと同等レベルの業界標準

ここは、抽象的な説明だけだと難しいところだが、本書では具体的な例で説明してくれているため、詳細はそちらで。
消費税の課税するという操作を例に、どこまでが抽象でどこからが具象かを上手く説明してくれている。

このパッケージ原則の章は、少し抽象的だが、本書を読めば解像度が上がると思う。これだけでも、普段の意識が変わると思う。しかし、具体的にどうしたら良いか、コードレベルでの解説はまだないため、続きを読むのが良い。後半の章で、具体的なコードとパッケージ原則も合わせた解説がある。

コメント

タイトルとURLをコピーしました