変更しやすく、保守しやすいコードを書くための5つの原則の頭文字をとって名付けられた原則
単一責任の原則
クラスはたった一つのアクターに対して責務を負うべきである
「アクター」はそのクラスを使用するユーザーやステークホルダー
→アクターが異なるクラスはアクターごとに分割するべき
違反すると..
- あるアクターのために行った変更の影響が別のアクターにも及び、バグが生まれてしまう。
- 変更前にどのアクターに影響があるかを調査する工数がかかる
- 変更後に全てのアクターに対してバグが発生していないかのテストを行う工数がかかる
- コードの共通部分を同時に変更してしまいコンフリクトする可能性がある
補足(dryの原則)
- don’t repeat yourself の略
- コードの繰り返しは避けろという内容
- DRYにするべきは概念の単位
- 同じようなロジックであっても、概念が違うものはdryにするべきではない
オープンクローズドの原則
ソフトウェアの構成要素は拡張に対して開かれていて、修正に対して閉じていなければならないという原則
- 拡張に対して開かれている
- 機能の拡張
- 新たなコードを追加することで機能を拡張することができる
- 修正に対して閉じている
- 機能を拡張するための既存コードの修正
- 拡張によって既存コードが修正されない → ソフトウェアの構成要素は の振る舞いは既存の成果物を変更せずに拡張できるようにするべき
特に分岐に関しても既存のコードに全く修正を入れずに機能を追加する方法
適用するべきケース
- 種別によって振る舞いの変更が必要な場合
- 会員ランク
- データの保存先の種類 →種別の拡張があった場合でも、既存のコード変更することなく対応可能
違反すると..
- 既存のコードに修正を加えると、バグを生んでしまう可能性がある
- 軽微な修正であってもケアレスミスをする可能性がある
- 既存のコードに対して再テストを行う工数がかかる
解決手段
拡張の可能性のあるものは抽象化し、具体の種別は抽象を実装する
リスコフの置換原則
サブタイプはスーパータイプと置換可能でなければならない
オブジェクト指向設計では一般的に継承は「Is-a」関係と言われている
- 「犬」は「動物」である
- 「正方形」は「長方形」である → コンパイルエラーは発生しないものの、振る舞いが意図されたものとは異なるため、正しい継承とは言えない
正しい継承 = 「Is-a」関係 + 「振る舞いの同等性」
この振る舞いの同等性に関する原則がリスコフの置換原則
簡単にまとめると意図しないような挙動を生み出すリスクをへらそうねの話
違反すると..
- 利用者が想定していない挙動によるバグが発生する可能性が高まる
- 利用者はスーパータイプとサブタイプは同じ挙動を期待して利用する
- 利用者がサブタイプまで全て理解した上で利用する必要がある
- リスコフの置換原則に違反したコードを使うと、オープンクローズドの原則に違反する可能性が高まる
振る舞いの変更に気付くには..
describe('rectangle test'),()=>{
test('Rectangle getArea',()=>{
const r1 = new Rectangle();
expext(f(r1,3,4)).toBe(12);
})
//Rectangleクラスを継承しているSquareクラスでも同じ結果になることを期待するためのテスト
//実際にはエラーとなり、これでリスコフの置換原則にいはんしていることがわかる
test('Square getArea',()=>{
const r1 = new Square();
expext(f(r1,3,4)).toBe(12);
})
}インターフェース分離の原則
インターフェースのクライアントにとって利用しないフィールドやメソッドへの依存を強制してはならない 言い換えると、不必要なメソッドの実装を強制してはならない
インターフェースや抽象クラスの抽象メソッドは未実装のままではエラーになる
→インターフェースに用意されている不必要なフィールドやメソッドにクライアントが依存しなくてもいいように、インターフェースは適切に分割すべき
違反すると..
- インターフェースに変更があると、実装側で使っていないメソッドである場合も修正しなければならなくなる
- 不要なメソッドを使わない(退化させる)ことで、リスコフの置換原則に違反する
- インターフェースが複数のアクターに使われる場合、単一責任の原則に違反する
依存性逆転の原則
- 上位モジュールは下位モジュールに依存してはならない. どちらもモジュールの「抽象」に依存するべきである
- 「抽象」は実装の詳細に依存してはならない。実装の詳細が「抽象」に依存するべきである
違反すると..
- 下位モジュールの変更が上位モジュールに影響を与える
- 下位モジュールがないと上位モジュールが開発できない
- 下位モジュールの拡張性・再利用性が低い
- 単体テストが困難