「良いコード悪いコードで学ぶ設計入門」メモ
これ何
良いコード悪いコードで学ぶ設計入門を読んで大事だなと思ったことを忘れないようにメモしておく。
良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方:書籍案内|技術評論社
個人的にはリーダブルコードよりも読みやすく、全てのコードを書く人におすすめできる本だと思いました。
各章ごと
3:クラス設計
- クラスは単体で作動するように設計する
- 自己防衛責務がある
- ベストプラクティス
- よい設計パターン
大事なこと:データとデータ操作ロジックを一箇所にまとめておくこと。必要な操作だけを後悔すること。
# example: 物体検出問題において正解データとなるbounding boxデータを表現するクラス class BoundingBox: def __init__(self, xmin: int, xmax: int, ymin: int, ymax: int, label: str): if not self._are_valid_positions(xmin, xmax, ymin, ymax): raise Exception("Invalid positions.") if len(label) == 0: raise Exception("Empty string is passed as a label name.") self.xmin = xmin self.xmax = xmax self.ymin = ymin self.ymax = ymax self.label = label return def _are_valid_positions(self, xmin: int, xmax: int, ymin: int, ymax: int) -> bool: # 座標のバリデーション if xmin < 0 or ymin < 0: return False if xmin >= xmax or ymin >= ymax: return False return True def get_bbox_area(self) -> int: # bboxの面積を返すメソッド # クラスに関連する処理はクラスのメソッドとして提供することで凝縮性を高める return (self.xmax - self.xmin) * (self.ymax - self.ymin) def slide_bbox(self, stride_x: int, stride_y: int): # bboxを移動するメソッド # 副作用を防ぐために、クラス変数を上書きするのではなく新しいインスタンスを作成する return BoundingBox( self.xmin + stride_x, self.xmax + stride_x, self.ymin + stride_y, self.ymax + stride_y, self.label )
4:不変の活用
- 再代入をできるだけ許さない
- 可変であることで生じること
7: コレクション
- 言語がコレクションに対する処理を提供している場合は自前で実装しない。
- ループ中に条件分岐が多くある場合には早期continueやbreakを活用する
- コレクションに関する実装が散らばってくるときにはFirst class collectionを検討する(カプセル化)
- コレクション型のインスタンス変数と、それらを不正状態から防御し正常に制御するためのメソッドを提供する。
@dataclass class Member: name: str hp: int # PartyはFirst Class Collectionとして機能する class Party: def __init__(self, members: List[Member]): self.members = members def add_member(self, new_member: Member) -> Party: # self.membersを上書きするのではなく、新しいPartyインスタンスを返却する new_members = copy.deepcopy(self.members) new_members.append(new_member) return Party(members=new_members)
8:密結合
- 単一責任の原則(クラスが担う責任はただ一つにするべき)を強く意識しておくことが疎結合性を担保するコツ
- 重複コードを恐れない。ほとんど同じコードであっても責務や概念が異なる場合にはコードを分割することは悪いことではない。
12:メソッド
- 他のクラスのインスタンス変数を変更しない。 変更するのは自身のインスタンス変数のみに絞る。
- コマンド・クエリ分離(Command-Query Separation:CQS)の原則を守る
- 状態の取得及び状態の変更のどちらかを責務とするメソッドにする。どちらも同時に行わない。
- 引数が多くなりそうなら別クラスにまとめることを考える
- 概念ごとにクラスに分割すれば引数が多くなりすぎることがなくなるはず
- 戻り値
- プリミティブ型で返すよりも独自の型を定義して返す方が安全
- エラーは戻り値で返すのではなく例外をthrowする
- 負数などでエラーを表現しない