良いコード/悪いコードで学ぶ設計入門を読んで
第1章 悪しき構造の弊害を知覚する
良くない設計がもたらす弊害
- 可読性の低下=>生産性の低下、不具合につながる
- そもそも不具合が混入しやすい
- 良くない設計が更に良くない設計を誘発する
生焼けオブジェクト
必要な初期化が完全に行われ切らない状態のオブジェクト
第2章 設計の初歩
特になし
第3章 クラス設計
関心の分離
高凝集=>カプセル化
クラス単体で正常に動作するように設計する
=> 部品であるクラスが品質的に完結していることでソフトウェア全体の品質が向上する
- 現実の営みにないメソッドは追加しない
スプラウトクラス
リファクタリングテク。既存のレガシークラスのレガシーメソッドの代わりに実処理を行う責務分離された新しいクラスを用意して既存クラスの既存メソッドが丸々処理を新しいクラス(スプラウトクラス)に処理を移譲する
第4章 不変の活用
特になし
第5章 低凝集
凝集度
データとロジックの関係性の強さ(強いほどよい)
staticメソッド
利用箇所を見極めよう(凝集度に関係ない処理)
- 目的別のファクトリメソッド
- 横断的関心事(※技術的な関心事)
デメテルの法則
あるオブジェクトが別のオブジェクトの内部を知るべきでない!!という思想
尋ねるな命じろ!
- メソッドチェーンする=内部のフィールドを知っていることになる
- 内部の変更に対して弱くなる!!
第6章 条件分岐
条件分岐の手続的表現の回避
条件分岐はinterface+αで排除する
※interfaceの命名は何の仲間であるか?
で考えると良い
- ストラテジーパターン(条件分岐そのものを記述コードから排除)
- 処理interface + map
- ポリシーパターン(条件の部分的な再利用等に活用)
- 条件interface + カスタマイズ用実装クラス
フラグ引数付メソッド
単機能のメソッドに分解せよ
第7章 コレクション
四角い車輪の再発名
今あるものより役に立たないものをつくりだしてしまうこと
第8章 密結合
結合度
モジュール間の依存の度合いを表す指標
これが高いとコードの理解が困難になり、変更時の影響範囲も大きく広がってしまう。
疎結合を守るために
- 責務を意識する
単一責任の原則
責任=誰が+どこまで(範囲)で表現される
あるクラスの責任範囲を一つに絞って設計していくと良い
※あるクラスが責任を多重に負うと、本来その責任を負うべきであったクラスが未熟になっていってしまう - DRY原則の正しい理解
共通化の罠
見た目が同じロジックだからと言って単純に共通化すると後の変更時に複雑さを持ち込む可能性が高い
繰り返されるべきでないのは同一概念の同一ロジック
安易に継承は使わない
スーパークラス依存が発生する
- サブクラスがスーパークラスの値や処理を使いたい際に、スーパークラスの構造を常に意識する必要性がある
- 逆にスーパークラスの構造変更時に全てのサブクラスで本当に調和が取れるのか?常に全てのサブクラスを意識する必要がある
- 単一責任の原則が意識されづらくなる
その他
影響スケッチ
依存関係を図式化したもの
ex) Jig
第9章 設計の健全性をそこなうさまざまな悪魔たち
YAGNI
You aren't going to need it (必要ないでしょう)
将来仕様を見越した実装は過剰実装になりがち
今必要なことにだけフォーカスしてシンプルに保つ
null
未設定状態をnullで表さないで、その状態も一つの具体的な状態であると認めて表現する
技術駆動パッケージング
ファイル間の関係性が見えない(どのファイルの内容がどのファイルの内容に関係しているのか?分かりにくい)
ビジネス概念は概念的なまとまりでパッケージングしていこう
心構え
設計にベストはなく、常にベターを探す
課題と目的を常に意識して正しい技術選択をするよう心がけよう
第10章 名前設計
名前を蔑ろにすると責務が入れ乱れる
関心の分離
関心(ユースケースや目的・役割)ごとに命名=>カプセル化=>疎結合高凝集
- 意味の広すぎるガバガバ命名クラスはNG
名前設計
-
具体的で、意味が狭く、目的に特化した名前を探す
- 目的=ビジネス目的
名前とは無関係なロジックが入り込みづらくなり、疎結合高凝集を実現しやすくなる
- 目的=ビジネス目的
-
存在ベースではなく、目的ベースで
-
ビジネス領域の目的をきちんと分析しよう
- 正しい目的が導き出せれば、正しい命名ができる
-
会話の中で使ってみて違和感がないか確かめよう
-
利用規約を読んでみる
- サービスのあり方を厳格に(※名称文言含めて)表現した有益なドキュメント
-
他の名前がないか少し深く考えてみる
-
モデリングの結果、実装されたクラスが実際に疎結合高凝集か確認してみる
名前の不吉なにおいポイント
- 仕様変更時に意味範囲が変化していないか?
- 会話に出てくる言葉なのにコードに出てこない言葉がいないか?
- 会話中で〜な〜と形容詞や説明的語句を多用して違いをつけているような表現はコード上でその違いが見分けられない状態である可能性がある
- 技術用語による命名
- ロジックを説明した命名
- ロジックではなく
目的や意図
を表現しよう
- ロジックではなく
第11章 コメント
コメントはコードに比べてメンテナンスされづらい
そもそもなくても伝わる状態がベター
コメントに書くべき内容
- 意図や注意点(コードから読み取れない内容)
第12章 メソッド(関数)
総じて、参照透過な関数とすることを大原則に
ロジックの設計
- 自身のインスタンス変数を使う
- 他のインスタンスのインスタンス変数を変更するようなケースは責務違反
- インスタンス変数を使わないメソッドはそもそも責務違反の可能性が高い
- 状態に対してイミュータブルに記述
CQS(コマンド・クエリ分離)
メソッド | 責務 |
---|---|
コマンド | 状態変更 |
クエリ | 状態取得 |
モディファイア | 状態変更&状態取得 |
引数設計
- 不変
- フラグ引数NG
- Null NG
- 出力引数(参照設定)NG
戻り値設計
- 型で意図を明示
- エラーは例外を
- 戻り値で表現するとダブルミーニング問題に繋がる
命名
- Vで表現できるように
- V+Oは他の何かを扱っている=>責務違反の可能性が高い
第13章 モデリング
モデルとは
- システム構造の説明
- 特定の目的達成のための最低限考慮が必要な要素を備えたもの
- 同じものをモデル化していても目的に応じてモデルは異なる
モデリング
物事を直接解釈して抽象化すること => ❌
目的の達成手段としての物事を解釈して表現すること => ⭕️
補足
- テクノロジーの本質は能力の拡大縮小
- 単一責任 = 単一目的(目的があるから→責務が発生する)
- モデルと実装(クラス)の関係は1:N
- 優れた仕組み(モデル)は優れた変換能力を有する
第14章 リファクタリング
テクニック
- ロジック(手続や条件式)は目的を表すメソッドに抽出すると読みやすくなる
リファクタリング時のテストコードをどう用意するか?
仕様が明確
- 新しいクラス作成
- テストクラス作成
- test faile
- 新しいクラス内部でリファクタリング対象の旧ロジックをコール
- test success
- 新しいクラスを実装
仕様が不明確
- 仕様化テスト
- 適当なinputによるテストコードで仕様を頑張って浮き彫りにする
- 試行リファクタリング
- プロダクションを直接リファクタリングしていきながら見えてきた仕様に対してテストを書いていく
リファクタリング注意点
- 機能追加とリファクタリング作業は別々に
- スモールステップで一つづつリファクタリング
第15章 設計の意義と設計への向き合い方
設計とは
何かしらのソフトウェア品質特性[1]の向上を促進するしくみをつくること
チャンク
脳の一度に記憶(意識)できる情報量の単位
- マジカルナンバー4 => 4±1程度
それ以上を意識させないクラス設計を心がけよう
設計の量的可視化
- Code Climate Quality
- SonarQube
費用対効果
- パレートの法則
- コアドメイン
よく使われる機能・よく変更される機能・重要な機能を見抜いて、効率的にコストをかけて設計を改善しよう
第16章 設計を妨げる開発プロセスとの戦い
粗悪なコードはきれいなコードを書くより常に遅い
- 割れ窓理論
- ボーイスカウトの規則 => 腐敗を許容してはならない!!!
レビューの心構え
chromium project 指針
正しければ何を言ってもいいは幼稚な考え方
普及
- 仲間を作ろう
- 小さく進める
- みんなで実感しよう
- ビジネスサイドにきちんと伝えよう
第17章 設計技術の理解の深め方
特になし