🍟

良いコード・悪いコードで学ぶ設計入門メモ

2022/06/18に公開

良いコード/悪いコードで学ぶ設計入門の備忘録として残しておく

https://www.amazon.co.jp/dp/4297127830/

単一責任の原則

単一目的の原則と置き換えると理解しやすい
特定の目的に特化して設計すること、そうすることで変更に強い高品質な構造になる
一般に現場では、責務という言葉が利用されるが、ピンとこない場合、目的と置き換えて考えてみると見えてくる

単一責任選択の原則

同じ条件式の条件分岐を複数書かないこと(一箇所にまとめられる)
本書では魔法種別(火属性なのか雷属性なのか)の判定のためのswitch文が散見されている場合のカウンターとして
魔法クラスを作り、その中で魔法種別の判定ロジックを定義し、使い回すことを推奨している

上記は、条件分岐のロジックをまとめられたことで一定の効果はあるが、条件分岐が増えていった場合に辛くなる
そこで、interfaceの特徴とMAPを利用することで条件分岐を書かなくても処理を切り替えられるストラテジパターンという設計手法が知られている

リスコフの置換原則

基本型(interface)を継承型(interface実装クラス)に置き換えても問題なく動作する必要がある
instanceofで型判定して条件分岐を書いている部分はリスコフの置換原則に反していることが往々にしてある

DRY原則の誤用

同じロジック、同じコードが出てきたからすぐDRY原則を引用するのは間違う可能性があるので注意
大事なのは、概念が同じであるかどうか
もし、概念が異なれば同じコードでも分けておく
そうしないと密結合になり、単一責任原則に違反してしまう

継承より移譲

継承は共通化文脈で使われるが、正確にクラスの責務・関心事を設計できてないと破綻する
破綻した継承は治すのが大変
最初から正確に設計することは難しいので移譲が推奨されている

高凝集だけでなく疎結合もセット

高凝集を意識すると関連してそうなロジックが1箇所に集まりがち
しかし、実際に蓋を開けてみると密結合になっているということは良くある
関連しているかを丁寧に判断して、疎結合化するよう常に意識すること

null問題

そもそもnullとは何かを理解しておく
未初期化状態のメモリ領域へのアクセスは制御トラブルの原因になる
こうしたトラブルを避けるためにnullは発明された
つまり、null自体は最低限のメモリアクセストラブルを防止するためのしくみでありnull自体は無効な扱い

よく何も持っていない状態や未設定状態を表すためにnullで初期化しているが、何も持っていない状態自体が立派な状態であり、nullではない
nullはその状態すら存在していないという概念であることを理解しておくべき

よって、そもそもnullを取り扱わない設計にすることが大事で方針としては

  • nullを返さない(nullをreturnしない)
  • nullを渡さない(nullを代入しない)

技術駆動パッケージング

設計パターンや構造的に似ている者同士でフォルダ・パッケージ分けをする方法のこと
設計パターンごとに分けると以下のようになる

- UseCases
  - 在庫ユースケース
  - 注文ユースケース
- Entities
  - 注文エンティティ
  - 発注エンティティ
  - 入庫エンティティ
- ValueObjects
  - クレジットカード番号
  - 注文先
  - 割引ポイント

この場合、UseCases配下を見ただけではどれが関係しているのか、関係してないのかが判別しずらい
それ故に、間違った用途で連携されバグを生む可能性がある
一方で、ビジネス単位で考えた際には以下のようまとめることもできる
この方が関連性の把握は容易

- 在庫
  - 在庫ユースケース
  - 入庫エンティティ
- 注文
  - 注文先
  - 注文エンティティ
  - 発注エンティティ
- 支払い
  - クレジットカード番号
  - 割引ポイント

Railsなど多くのWebフレームワークでMVCアーキテクチャが採用されている
そのため、MVCをお手本にしたフォルダ構造になりがちなので、これも技術駆動パッケージングの1種であると言える

命名

関心事を意識する

命名は大事。抽象度が高いと密結合しやすくなってしまう
関心事沿って命名することでこれを回避できる
例えば、「商品」という概念に、注文品、予約品、在庫品、発送品をまとめちゃいがち
関心事に目を配ればそれぞれ独立して定義すべき

目的ベースで名前を考える

ECサイトにおいて、「住所」だと抽象度が高い
実際に住所を使うシーンとしては「発送先」「発送元」が考えられる
金額も同様に、消費税、請求金額、延滞料など目的毎に落として考えるべき

ラバーダッキング

脳内にあることはぼんやりしていることが多い
声に出したり、意見を交換する中で脳内が整理されて適切な命名が出てくることがある
デバッグ時に相手に説明していく中で自分で解決方法に気づいて、自己解決するという経験はあると思う
このデバッグ手法をラバーダッキングという
命名に関しても応用できるという話

CQRS(コマンド・クエリ責務分離)

アーキテクチャパターンの1種
参照系の責務はDBから値を取得するだけの処理で、画面表示用に用いられる
単に取得して表示するだけの責務であるため、計算やデータの変更は伴わないのが重要
この用途であれば、DBの値を格納して表示側に転送するだけのクラス(DTO)として実装する
DTOはデータ転送用途に使われる設計パターンと理解しておくこと(更新系ではつかってはいけない)

退化コメント

コードと比べてコメントはメンテナンスされにくい
そのため、コメントが古くなり実装と乖離していく
この嘘を付き始めたコメントを退化コメントという

コメントの書き方

読み手が気にするのは

  • 「このロジックはどういう意図で動いているか?」
  • 「何に注意すれば安全に変更できるか?」

リファクタリングは別PRにすること

機能追加のPRと一緒のPRにしない
作業をする時は2つの帽子(ファンクションの帽子・リファクタリングの帽子)をかぶらないことという書籍「リファクタリング」の教え

コード品質を評価してくれるツール

改善を行うための仲間集め

マーケットシェア理論をベースに影響力を持てるレベルまで仲間を集める
市場占有率が10.9%に達すると影響力を無視できない存在として認識されるという理論
これをチームに応用する20人のチームであれば2人いれば良いという計算
なぜ、仲間が必要かというと品質のマズさを指摘しても一人だと無視されることが多いから
指示以外のことをやっているとネガティブな見方をされてしまう

フォローアップ勉強会

インプットよりもアウトプットの方が学習効率は高い
インプットだけの勉強会はバッドノウハウである
本の読み合わせ「だけ」をするは学習効果が低い。悲しいことに、参加者は一度に大量の情報をインプットだけしてもすぐ忘れてしまうだけ。
具体的なフォローアップ勉強会方法は以下

  1. 本に書かれているノウハウを1,2個読み合わせ
  2. ノウハウを適用できそうな箇所を、「プロダクションコード」の中から探す
  3. ノウハウを使ってコード改善
  4. どう改善したかについて、before、afterがわかるように発表する
  5. 発表内容について質疑応答や議論する

Discussion