「リファクタリング 既存のコードを安全に改善する」を読む 2

その1

第2章

  • 機能追加とリファクタリング
    • 「重要なのは、どちらの帽子をかぶっているのか常に意識しておくことです」
    • 機能追加
    • リファクタリング -> 外から見た振る舞いを変えずに、内部を理解しやすく、変更しやすくする
      • 機能追加しない、テストは変えない
      • リファクタ前にテストが足りなければ足すけれども、リファクタリングで外から見た振る舞いは変わらないのだから、テストは変えなくていいはず
  • 「コードを理解しようとする時には、常にリファクタリングが役立ちます」
  • 間接層とリファクタリング
    • 「1つのものを2つに分割するということは、それだけ管理しなければならない部分が増えるということです。間接層は、最小限に絞り込むべきです。」
  • リファクタリングと設計(めっちゃいいこと言ってる、最高
    • 柔軟性を求めた設計は過度に複雑になりがちで、作りづらく、保守しにくく、苦労して作ったわりに使われなかったりと挫折感がある
    • リファクタリングを前提として設計すると「妥当な設計」でよくなる(「完璧な設計」じゃなくて良い
    • 柔軟な設計を作り込む代わりに「リファクタリングしやすいシンプルな設計」を考える(柔軟な設計より比較的簡単)
  • 特性の横恋慕
    • データと振る舞いを1つにまとめるのも重要だけど、振る舞いのみの変更に対処するために、データ/振る舞いの2つに分けた方がいいこともある。(ただし、回りくどくなるという欠点もある)
      • memo: 初めから2つに分ける派だけど、人に説明する時に便利そうな文だと思った

第4章

  • 単体テストと機能テスト
    • 単体テストプログラマの生産性を向上するために行うものです。それで品質が上がるのは嬉しい副作用にすぎません。
    • 機能テストはソフトウェアの品質を保証するためのもので、プログラマの生産性とは無関係です。
    • 機能テストや実用でソフトウェアのバグが見つかったら、バグを明らかにするための単体テストを追加すべきです
  • 完全なテストを求めるのではなく、怪しいと思う部分をテストすること。
  • 多大な時間をかけて全てのバグを見つけるより、適切な時間で大部分のハグを見つけること

6〜11章

  • 特に目新しいのはなく「あるある〜」という感じだったけど、改めて「やっていいんだな」という許しを得られた
  • たまに「自分はやらん」というのもある
    • ポリモーフィズムによる条件分岐の置き換え(p255)、サブクラスの抽出(p330)
      • memo: まったくやらん訳では無いけどすぐ選ぶ選択肢でも無い。なぜなら私が継承の柔軟性を信用していないから
    • Template Methodの形成(p345)
      • memo: 継承前提の実装はTemplateの把握に苦しんだり、意図から外れた使い方されると途端に取り回しが困難になったりするので...
  • 苦肉の策よかった
    • 外部メソッドの導入([p162)
      • サービスクラスを拡張したいが許されない場合、サービスクラスのインスタンスを元に頑張る方法
    • 局所的拡張の導入(p164)
      • 1クラスに対して「外部メソッドの導入」を複数大なう場合は、継承 or 移譲 を作る
  • データクラスによるレコードの置き換え(p217)
    • memo: データクラスよく使うんだけど、「データクラス = 振る舞いが無い、オブジェクト指向じゃない、やめるべき」みたいな風潮あるの?

「リファクタリング 既存のコードを安全に改善する」を読む 1

ちゃんと読んでない本が本棚にたくさんあるので、感想文駆動読書をやる。

第1章

  • 普段やっていることではあったけど改めて「やって良いんだな」という許しが得られた
  • リファクタリング中はパフォーマンスに神経質にならなくて良いんですよ
    • リファクタ後の方がチューニングすべき箇所分かりやすいし、最悪リファクタ前に戻すとしても容易に戻せる
  • p34: "他のオブジェクトの属性を調べる switch 文を書くことは、常に間違いです"
    • 強い主張だ...
  • ポリモーフィズムによる条件分岐の置き換え
    • 継承を嫌いすぎて手段にもあげてなかったけど必要な状況あるよなー必要になったら使おうなーという気分

カタログ部分

  • メソッドの抽出(p110)
    -> 長いメソッドは意味ごとに分割しようね

    • メソッド名が説明的で長いとしても気にしなくて良い。たとえ抽出した結果全体の文字数が増えることになったとしても抽出することで明快さが向上するなら抽出すべき。
    • memo: 「結果コード量増えてるやん、いいんかこれ...」と思っても「分かりやすくなるなら良い」という許しがある
  • 問い合わせによる一時変数の置き換え(p120)
    -> 式の結果を保存してるローカル変数はメソッドに切り出せる

    • メソッドを実行するごとに同じ計算を行うので、計算効率が気になるかもしれないけど、気にしなくてよい。問題になってから考えれば良いし、細かく分けている状態の方がより良い最適化を見つけやすい。最適化が見つからなかったとして、メソッドを一時変数に戻せばいいだけ(簡単)。
    • memo: 効率のことは後で考えるで良いという許しがある

「PHP7 で堅牢なコードを書く」 のスライドを見て

PHP7 で堅牢なコードを書く - 例外処理、表明プログラミング、契約による設計 / PHP Conference 2016

一年前にも見てその時は曖昧な理解だったけども、今見たら「それありがたい!わかる!そういう名前ついていたの...あ、そういう考え方もあるのか、なるほど...」となった

嬉しみ

  • 防御的プログラミング
    • やって欲しいことをやりやすく、やって欲しくない事をやりづらくする
    • 型による防御
  • 攻撃的プログラミング
    • 中途半端に動くより死んだ方がマシ(原因を特定しやすい、影響が小さく済む)な時にやる
    • 正当性と堅牢性
      • 個々のクラスは正当性を重視する
  • 表明プログラミング
    • 表明は、実装するエンジニア間で必要なコミュニケーション
    • 副作用があってはいけない。本番ビルドで無効になる表記をする。(-> assert)
  • 契約プログラミング
    • 契約プログラミング = 正しさを保証する
      • 状態P(事前条件) の時に プログラムA を実行したら、必ず 状態Q(事後条件) になる
    • 責任の所在
      • 事前条件を守れていない場合はプログラムAを "利用している側" がバグの原因である
      • 事後条件を守れていない場合は "プログラムA(が中で利用している処理)" がバグの原因である
    • バグ or 例外
      • 常に失敗する条件は"バグ"
        • etc) 間違ったSQL文(=バグ)が渡されたら Error を投げる
      • たまに失敗する条件は"例外"
        • etc) ネットワーク障害(=例外)の場合は Exception を投げる

雑感

  • 防御的プログラミング
    -> 手法の名前は知らなかったが、会社の人がやっていたのを真似して常日頃嬉しみを感じていた部分で、名前知れてよかった。
  • 正当性と堅牢性
    -> 正当性について自分の認識が曖昧だったのが更新された。対で考えてなかったけど、片方を上げればもう一方が下がる対の存在だよなぁ。堅牢性はViewの所でいい感じにやるのが良いかな、というかそうしていたように思う。
  • 表明プログラミング
    -> 集約のスライドでも見てなんとなく良さげ〜ぐらいしか思っていなかったけど使いどころが腑に落ちた。(assertの使い所いまいち分かっていない問題も解決した)
  • 契約プログラミング
    -> 日頃作っていて責任の所在に悩むことがあり、所在に悩むと値チェックもエラーハンドリングもどこでやるべきなのか悩んで困るのだけど、分け方が腑に落ちた(多分)

「わかる!ドメイン駆動設計」読んで得た嬉しみ

https://booth.pm/ja/items/392260

技術書店の流れでBOOTH見たら見かけ、「DDD知らなすぎてやばい」と思っていたので読んだ。 ドメインとコンテキストの説明がわかり易くて嬉しい本だった。

ドメイン

■ コンテキストマップ

  • コンテキストが異なるとユビキタス言語も変わる
  • コンテキストマップは「現状のまま」を書くもの。理解と共有を深めるためのものであり、完璧を求めて理想像を書くものではない

巨大な泥団子を腐敗防止層で包みつつ徐々に切り崩していくの、それしかないんですよね、はいって感じ。

■ 他

  • ドキュメントは腐るからそれより説明のいらないコードを書くべし
    • コードが仕様書じゃ
    • (お、これ DbC かな?となった)
  • ファサードパターン
    • (集約、これのことでは となった)

有料の情報源の感想(?)ってどこまで書いていいのか分からんくてわからん...

DDD全然知らないくせに集約についてのスライドを見たまとめ

集約の設計と実装 // Speaker Deck

DDDに関する知識はほぼ無だけどノリでいけるっしょの精神で読んだ。コードもノリで読んでいる。
ちょこちょこ嬉しみポイントがあったので理解用まとめ。間違いもあるだろうと思う。
(嬉しみ = 普段悩んでいることに対して道がひらけた感があった) 書き終わって見返すと、「境界という概念(現実ままでなくて良い)」と「表明プログラミング」が嬉しみ高かったように思う

■ 集約とは
  • 関連するオブジェクトをひとかたまりとし、データを変更する単位(集約)として扱う
  • 集約は変更の一貫性、整合性を保証する
  • 集約が持つデータの範囲/集約の責務の及ぶ範囲の定義が大事
    • (この範囲を境界と言っているはず)

■ ルートエンティティ

  • 集約は、ルートエンティティを1つ持つ
  • 集約への変更はルートエンティティを通す
  • 集約の中のある子エンティティXを見つけたい時もルートエンティティを通す

■ エンティティXの同一性の管理

  • 集約の同一性を見たい場合は、ルートエンティティを比べれば見れる(はず)
  • 子エンティティXは同一性を気にしなくて良い。 なぜならば、
  • 子エンティティはルートエンティティから辿れるはず
  • すべての同一性を保とうとすると整合性管理が大変(だから、集約がある)

■ 集約の範囲(境界)の決め方

  • 小さく作る
  • 他の子エンティティと整合性を保つ必要があるものだけで集約を分ける
  • ただし 現実世界とは分けて考える。 ユースケースを無視しないで作る
    • 車にエンジンが付いているからといって、2つを同じ集約にしていいわけではない
    • 車とエンジンを切り離して「同一性を管理したい/変更を追跡したい」という要件があるのならば集約を分けるべき

■ ルートエンティティ/子エンティティ 詳しく

  • 一貫性/整合性の担保を実際におこなうのはルートエンティティ
  • なので、集約への変更は全てルートエンティティを通して行わなければならない
  • 子エンティティに変更を加える時もルートエンティティを通す
  • 子エンティティに別集約の参照を持たせたい場合、ルートエンティティを間接的に持たせるに留める
    • 「集約Aに変更を加えたら集約Bも変更された...」とかを避けるため
    • 参照を持たせないことで、別の集約の変更を行えないことを保証する
    • 「集約Aと集約Bは同時に変更されるものである」という要件ならば、集約Uを別で定義する
    • 集約Uはさすがにポインタで別の集約をもつっぽい
    • ChildEntity#doSomething() に引数で渡して 一時的な使用は可能

■ 契約による設計

  • 正しさ の書き方は「"状態P"の時に"処理A"を行なうと、必ず"状態Q"になる」という書き方が良い

    • それを実装でやるのが契約による設計(DbC)
    • ただしこれは制御構造ではない
      • (状態Pじゃないと処理Aをしない、ということではない?)
    • 仕様の明確化である
  • Modelが状態Qにならなかった場合、疑う対象は2つ

    • Modelへ指示した側(= 状態Pじゃないのに処理Aを依頼した)
    • Model(= 状態Pの時なのに状態Qにならなかった)