良いコード/悪いコードで学ぶ設計入門をざっくり読んでみた
最初に
ミノ駆動さんの良いコード/悪いコードで学ぶ設計入門を読んでいる際に、個人的につけていたメモです。
いわゆるまとめ記事ではなく、内容も飛び飛びで読んだときの自分の感想がメインの記事となっています。
とっ散らかった文章ですが、参考になれば。
本の感想ですが、理論的な話と具体のバランスがとても良かったです。最後の方の章では組織論的な部分にも触れられており、読みやすかったです。やはり心理的安全性とコミュニケーションは大事ですね。
データクラスと利用する計算ロジックをなるべく分けない
分けることで、コード全体の可読性が下がってしまう。可読性が下がると、ある改修による影響範囲の調査コストが増加したり、既に別の場所で実装済みのロジックを再度実装する「車輪の再開発」が発生しうる。
コードの重複によって、コード全体の変更容易性が下がり、負のループに入ってしまう。
本冒頭に書かれていたように、変更容易性を獲得するための設計が良い設計だとすると、其れの逆はアンチパターンとなる。
そもそも変更容易性とは何ぞや?というのは16章に詳細が書かれているように、保守性のなかでの修正性を意味し、変更に強いかどうかを表す指標ということになる。
脱生焼けオブジェクト
インスタンス化しただけではすぐ使えず、インスタンス化した後に何かしら追加の処理をして初めて後続の処理で利用できる設計になっているクラスを生焼けオブジェクトというらしい。いったんインスタンス化した後にセッターで値を設定してから後続の処理に入らないとだめなパターンも当てはまりそう。
必須の値だけでも初期化時に絶対入るように設計しておいて、オプションの値は任意で設定して回る、というのが良いバランスなのかな。
ガード節の使いどころ
実装したメソッドが予期せぬ引数を渡して実行されている場合、それをバリデーションしてから不正であると判断したら早期リターンしてバグの混入を防ぐ、という例が紹介されていた。
ここを読んだときに、呼び出される側は正常な値が入ってくることを前提として実装し、呼び出す側が正常な値かどうかを事前にチェックするようにする設計が望ましいという話も以前どこかで読んだなぁと思いだした。
契約プログラミングのことだった。
- 事前条件:呼び出し側が守るべき条件
- 事後条件:呼ばれる側が守るべき条件
- 不変条件:クラスやモジュールが常に満たすべき条件
これに基づくと、いわゆるバリデーションを行うのは呼び出し側でやるべき、ともいえそうだけど、絶対にケースバイケースなので、いろいろ調べる必要がありそう。
共通して言えるのは、予期せぬ呼び出しを行わないようにチェックをどこかしらに実装してバグの混入を防ごうということで、その処理をどこに実装すべきかは議論の余地がある、というところだろうか。
DRY原則の誤用
重複を避けるために共通化できそうな部分はくくりだして親クラスのメソッドとして昇格させたりなど、DRY原則を守るようにプログラミングをする、というのは重要である一方、不用意な共通化はバグを生み出す。
たとえ似ていた処理であったとしても、たまたま似ている構造であり目的が全く違うものであれば共通化しないことが望ましいというのは達人プログラマーにも書いてあり、まったくもって同意だけど、あえて逆張りというか難癖をつけると、目的が同じか異なるかの判断基準はどう持てばよいのだろうかと考えた。
考えるときのレイヤーを抽象的な部分で考えたら広い意味で同じ目的だねともいえそうだし、かといって具体の方に寄せすぎるといつまでたっても重複が取れないなぁとも思った。
もちろんどちらも極端な話であって詭弁だし、それらの判断基準をどう持つかが熟練度(いわゆる慣れ)なのだと思うが。
単一責任の原則やDRY原則といった考え方のフレームワークを組み合わせて高度な推測を行って感覚を養っていくしかないのだろうか。
interfaceを駆使する
分岐処理を実装する際にswitchやifを使いこなすのは簡単であるが、何度も改修を行う部分であれば次第に肥大化しバグの温床となる。
重複を取り除き、コードの見通しをよくするためにもinterfaceを駆使することが初級者から中級車へのステップアップとなる。
interfaceを設計する行為はif文などのべた書きの処理をその名の通り抽象化して定義する行為なので、エンジニアがステップアップするためには会得しておくべきスキルだと感じる。
interfaceをきちんと定義しておくことでの実装漏れのエラーを検知でき、実装が自然と縛られるのがGoodだと思う。あとから開発に入った人は既存のコードをまねて書くため、ある程度型として縛ってくれるinterfaceはそういった面でもコードの内部品質を向上させることに寄与するのかもしれない。
存在駆動でなく目的駆動で名前を付けよう
人物など、存在するものにそのもののみに着目した命名は無味乾燥なものになり、もともと何を解決するためのクラスなのかがぼやけてしまう。
目的がぼやけていると本来別々で管理する目的があったとしても、改修が無秩序に行われることになりかねない。
そこで、どんな業務的があるのかという理解を深めたうえで、名前設計を行うというアプローチが推奨される。モデリングもまさにこれに当たる。
あらゆるシステムが業務上の問題を解決するために存在する以上、どんなビジネスロジックをシステムに落とし込もうとしているかという前提を理解せずに名前を付けると無秩序になるので気を付けたい。
また、業務目的を洗い出す営みは定期的に行って継続的にモデリングしないと陳腐化するのだろうと感じた。
モデリングをしてクラスに落とす
クラスの構造に問題がる場合は以下の手順に従ってモデリングを行い、真の目的を明確にしたうえで実装に落としていく。モデルは目的を達成するために最低限必要と思われるものを一覧化したものになるため、実装上で抜け漏れが見つかることがある。抜け漏れが見つかり次第モデルにフィードバックし、精緻化していく。
- モデルが達成しようとしている目的を洗い出す
- 其れぞ乗れ目的に特化したモデリングをやり直す
- 目的駆動名前摂家に基づき、モデルに対する命名を行う
- モデルに目的外の要素が入り込んでいる場合、見直しを行う
システムは目的達成手段であり、モデルがシステムの一部であるということはモデルは目的達成手段の一部である。モデルで達成すべき目的を洗い出して、目的を達成するために分担すべき責務をクラスに分けてコードに落とす。現実世界という極めて具体的な事象からモデリングによってエッセンスを見つけ出したうえで、再度具体としてコードに落としていく一連の流れを繰り返し行うことが重要だなと思った。
Discussion