#paper
[https://staff.cs.utu.fi/~jounsmed/doos_06/material/DesignPrinciplesAndPatterns.pdf](https://staff.cs.utu.fi/~jounsmed/doos_06/material/DesignPrinciplesAndPatterns.pdf)
2000, Robert C. Martin (Uncle Bob)
# なぜこの論文を読んだか
SOLID原則について改めて説明するタイミングがあって原著ってなんだろう?となって調べてたところ2000年にすでに Uncle Bob が書いたものが出てきたので読んでみた。
読んでみると、SOLID原則だけではなくパッケージの構成やパッケージ間での結びつきのあるべきなど、現代でも有用な設計に関する議論が多く書いてあった
# メモ
ソフトウェアが腐っていくのにはいくつか要素がある
- Rigidity : 変更の難しさ
- Fragility : 1つの変更をしたら様々なところが壊れてしまう脆さ
- Immobility : 他のプロジェクトや同じプロジェクト内で再利用できない
- Viscosity : 最適な変更が施せない
- design: 通常プログラマは、1つのことをやる際に複数の選択肢が思い浮かぶ。そのうち最適に見えるものが適用できない状態
- environment: コンパイルが遅いなどがあると、コンパイルが遅くならないような局所最適な変更を加えてしまう
これらの腐敗は、主に以下の2つによってもたらされる
- 変化し続ける要求
- 戦略的ではない依存の追加、変更
この論文の中では、クラスレベル、パッケージレベル、パッケージ間レベルそれぞれで使える原則が紹介されている
クラスレベルのものは、いわゆる SOLID原則として知られているもので、この当時は S の記載がない。 (そもそも SOLID原則と言い始めたのは、Uncle Bob ではなく他の人という噂もあるらしい)
## Principle of Object Oriented Class Design
クラスレベルの実装の原則の話
### The Open Closed Principle (OCP)
> A module should be open for extension but closed for modification
Bertrand Meyer (プログラミン言語 Eiffel の開発者) が最初に言い始めたもの。
kotlin
```
if(modem.type is Modem.Ernie) {
} else {
}
```
このように利用側に様々な型情報などが露呈している状態は、他の型が追加された際に利用側も変更しないといけないという点においてこの原則に違反している。
この原則をうまく守るためには、ポリモーフィズムを利用して利用側は型の追加を気にすることなく拡張できる必要がある。
### The Liscov Substitution Principle (LSP)
> Subclasses should be substitutable for their base classes.
Barbar Liscov が提唱した原則。
Bertrand Meyer が提案してた Design by Contract にも大きな影響を受けている。
継承の矛盾を表現する有名な、円 is a 楕円問題を通してこの違反を見る。
楕円は、「2点の焦点とそこからの距離の和」によって構成されるので、フィールドとして2つの焦点とそこからの距離の和を持っている。つまり円を「2点の焦点が同一の点である」という特別な場合になっている。
kotlin
```
class Ellipse {
var pointA: Point
var pointB: Point
var majorAxis: Double
fun setFoci(a: Point, b: Point) {
pointA = a
pointB = b
}
}
// (a, b) をセットするときにどちらも a にセットする
class Circle : Ellipse {
override fun setFoci(a: Point, b: Point) {
pointA = a
pointB = a
}
}
```
こういった実装をすると、以下のように Ellipse は満たすのに Circle は満たさないみたいな状況が発生しうる
kotlin
```
fun testSetFoci() {
// arrange
val e = Ellipse()
val a = Point(-1, 0)
val b = Point(1, 0)
// act
e.setFoci(a, b)
e.setMajorAxis(3)
// assert
assert(e.a == a)
assert(e.b == b) // Circle ではこれを満たせない
assert(e.b == b)
}
```
これを Liscov は、 Design By Contract の観点で説明していて LSP を満たすということは、
1. Its precoditions are no stronger than the base class method
2. Its postcondition are no weaker than the base class method
としていて、 `expect no more and provide no less.` と言っている
### The Dependency Inversion Principle (DIP)
> Depends upon Abstractions. Do not depend upon concretions.
Concrete なものは変化が Interface に加えて激しいので抽象に依存するほうが良い。
### The Interface Segregation Principle (ISP)
> Many client specific interfaces are better than one general purpose inteface
Client がたくさんいる場合には、その利用用途に応じて Interface を切りましょうという原則。
Client というのはわかりづらいが、このクラスを利用する他クラスみたいなニュアンス。

## Principles of Package Architecture
設計というスコープではクラスだけでは不十分でここでは `How do we choose which classes belong in which packages` に答えるべき原則を紹介する。
Package Cohesion Principle として知られている3つの原則を紹介。
REP や CRP は、再利用性の観点から有用な原則で、CCP はメンテナにとって有用な原則になっている。
これらや時間やcontextによって何を重視するかが変わるのでパッケージの設計も不変ではない。
### The Release Reuse Equivalency Principle (REP)
> The granule of reuse is the granule of release
リリース可能な単位でパッケージを構成する。
`Since packages are the unit of release, they are also the unit of reuse` という観測から来る原則。
### The Common Closure Principle (CCP)
> Classes that change together, belong together
変更のライフサイクルでパッケージを構成する。
同じタイミングで変更されると予測されるものを同じパッケージに入れようという原則。
### The Common Reuse Principle (CRP)
> Claases that aren't reused together should not be grouped together
REP に近いが、共通で利用しているようなものだけをパッケージングしないと、関係ない変更によってこれを利用している依存先にも変更が影響してしまいかねない。
## The Package Coupling Principles
パッケージ間の関係性についての原則
### The Acyclic Dependencies Principle (ADP)
> The dependencies between packages must not form cycles.
循環的に参照してしまうような構造は、影響範囲を広めてしまう。
`Protocol` から見たとき、 `Comm Error` だけに依存していて、 `Comm Error` も他に依存先はないので影響範囲が `Protocol` と `Comm Error` に限定される。

しかしながら、 `Comm Error` が `GUI` に依存してしまった瞬間に `Protocol` は推移的にアプリケーション全体に依存するようになってしまい影響範囲が全体になってしまう。またテストや部分的にプログラムを実行する場合にもすべてのパッケージが必要になってしまう。

こういった場合には、DIP を用いて、依存性の逆転させて影響範囲を狭くする。

### The Stable Dependencies Principle (SDP)
> Depend in the direction of stability
`Stability is related to the amount of the work required to make a change`
様々な要素がパッケージの変更を難しくするものの1つ確実なのは、他のパッケージからたくさん依存されることである。
この辺の定量化に有効なメトリクスとしては、
Instability = (outgoing dependencies) / (incoming dependencies + outgoing dependencies)
### The Stable Abstractions Principle (SAP)
> Stable packages should be abstract packages.
Abstraction = number of classes / number of abstract classes
この原則は、SDP にも依存する原則ではあるが、安定しているパッケージは抽象度が高くないといけないというもの。

左上 (Abstract かつ Stable) から右下 (Concrete かつ Instable) の線上に位置するようなパッケージを目指しましょうという原則。
> Package is abstract in proportion to its incoming dependencies and is concrete in proportion to its outgoing dependencies
という原則
## Patterns of Object Orienrted Architecture
上記のクラスレベル、パッケージレベル、パッケージ間レベルの原則を適用するにあたって同じようなデザインパターンが存在しているのでそのパターンを紹介する (多くは、 GoF からの引用)
### Abstract Server

DIP を利用して、利用側と利用される側を分離する
### Adapter

サードパーティーや他のサービスに依存している場合には、Adapter を入れて腐敗防止層を作る
### Observer

イベントの通知とそれをトリガーにした処理を分離する
### Bride

「何を」「どうやって」行うかは分離しておくパターン
### Abstract Factory

User側はポリモーフィズムとして Modem を扱い、どの具象クラスの作成は、ModemFactory の実装が行うパターン
# 感想
- SOLID原則についてもうちょい読みたいなと思って読み始めたものの、SOLID原則での話よりもいろんなことが書いてあってしかも現代でも例えばマイクロサービスやモジュラモノリス設計で活かせそうな話が書いてあって意外な収穫だった
- とはいえ多くは、自分の理解している内容でそこに言葉がついたり改めて体系立てて整理してくれたりみたいな感じではあった
- Effiel の作者の Bertrand Meyer も割とこの時代に生きて Uncle Bob とかに影響与えてたのかーなどと適しを感じることもできた