読者です 読者をやめる 読者になる 読者になる

「オブジェクト指向設計実践ガイド」を読んだ

先日 「オブジェクト指向でなぜつくるのか」を読んだ が、内容が薄かったのでもっと濃いオブジェクト指向についての本が読みたかったので、先輩に勧められたこともあり読んでみた。
内容は濃くて良かった、こういう本が読みたかった。

特に良かったのが「オブジェクトがあるからメッセージを送るのではなく、メッセージを送るためにオブジェクトがある」とのところ。
自分はそんな真面目に考えたことはなく、軽い気持ちでクラスを定義しメソッドを作成してしまうので、そうではなく相互関係をちゃんと検討した上で実装すべきなんだなと思った。
そういうメッセージベースの考え方ができるようになると一段上に上がれるとのことなので、意識していきたい。

また、クラス内で具象化してしまうと依存度が高くなってしまい良くないので、 DI して依存度を外から設定できるようにしよう、というのが繰り返し述べられていた気がする。
ここはテスト書くときにほぼ必須なので最近気をつけている。

1つ疑問だったのが、この著者は静的型付を「コンパイル時間がかかる割にメリットが少なすぎる」と言って動的型付を持ち上げているところ。
別にそれはいいのだが、静的型付だったら何も意識しないで済むところを動的型付はテストを書くことでカバーしていて、静的型付にもメリットがあるところをちゃんと書くべきでは、と思った。

全体的な内容は良かったのだが結構難しく、正直あまり理解できない部分が多い… 3ヶ月後くらいにまた読み直そうと思っている。

以下 wikihub 日報 に雑に書いてた感想まとめ。


読み始めた。 Rubyオブジェクト指向についてガチで学ぶ感じになりそう、うわべだけのオブジェクト指向 ではなく真髄に迫る感じがして良い。

  • オブジェクト指向設計とは、「依存関係を管理すること」
  • 設計がないと管理されてない依存関係が大混乱を起こし、1つ変更すると他のが壊れてやばくなる
  • 設計は芸術と言えます、コード構成の芸術なのです。
  • SOLID
  • 前もって詳細設計を作るのは全く意味がない
  • アジャイルが成功するかはシンプル、適応性、柔軟性のあるコード
  • アジャイルは「どれだけの期間で完成するかわからない」を肯定するものであり、オブジェクト指向だってそう、完成時期はわからない

25%まできた。
ためになる!(いつもためになるって言ってる気がする)

Gear と言うクラスを愚直に作った後に徐々に綺麗にしていく流れ。
自分はメンバ変数をいろんなところで叩いてしまうのだが、この本によるとアンチパターンっぽい…。

  • 単一責任のクラスを設計しよう
  • シンプルであれ
  • 設計とはアプリケーションの可変性を保つために技巧を凝らすことであり、完璧を目指す行為ではない
  • TRUE
    • 見通しが良い(Transparent)
    • 合理的(Reasonable)
    • 利用性が高い(Usable)
    • 模範的(exemplary)
  • TRUE なコードを書くために、それぞれのクラスが、明確に定義された単一の責任を持つ ように徹底すること
  • 2 つ以上の責任をもってはいけない
  • クラスが実際に何をしているか、1文で表現できるか?
    • それと、またはみたいな言葉が出たら2つ以上の責任を負っている証拠
  • 設計を決める時、何もしないことで将来的なコストが変わらないなら何もしないで良い
  • 今と未来の可能性のトレードオフを理解しコストが最小になる決断をする
  • 変数はそれらを定義しているクラスからでさえ隠蔽しましょう
    • ruby の attr_reader を使う
    • ジャバで言うと getter, setter かな?
  • メソッドもクラスのように単一の責任を持つべき
  • 1つのこのに専念するクラスは、その1つのことをアプリケーションの他の部位から「隔離」する
  • クラスが知るべきことは自身の責任を果たすために必要十分なことのみ
  • dependency injection で依存をクラスの外にだす
    • Ruby は Interface ないから DI できないと思ってたけどそもそも動的型付だから Interface とかいらないんだった…
  • 場合によっては依存を隠すのではなく、逆に依存部分を強調することでわかりやすくする
  • 初期化の引数にハッシュやデフォルト値を使うとわかりやすい
  • 初期化の引数を変えられない場合(3rd party ライブラリとか)はファクトリーでラップすると良い

オブジェクト思考はクラスがどうこうではなくメッセージをどう送り合うか、と言うことらしい。
正直あまり理解ができてない点が多く、自分は全く理解できてないだなと言うのを理解できて良い。

  • 自身より変更されないものに依存しなさい
    • 具象クラスは抽象クラスよりも変わる可能性が高い
    • 多くのところから依存されたクラスを変更すると広範囲に影響する
  • 変更が多くかつ依存するものが大量にあるとやばい
  • 依存関係の管理で鍵になるのはその方向を制御すること、また自身より変更の少ないクラスに依存するべき
    • Clean Architecture の思想は依存を一方方向にするしそう言うことなんかな
  • オブジェクト指向アプリケーションは「クラスから成り立つ」がメッセージによって「定義される」
  • クラスが何を「する」かではなく、何を「明らかにするか」
  • クラスに基づく設計ではなくメッセージに基づく設計ができるようになるとキャリアの天気になる
    • このクラスがどうするでなく、このメッセージを送る必要があるがどうするかみたいに考えられるようになれ
    • オブジェクトが存在するからメッセージを送るのではなく、メッセージを送るためにオブジェクトは存在する
  • public, private などは明確に意図を持ってやれ
    • rails は private method の先頭に _ をつける (知らなかった…
  • デルメルの法則
    • オブジェクトを疎結合にするための規則
    • 異なる二つ目のオブジェクトを介すことを禁じる (ドットで2つ以上繋ぐな、 music.song.artist とかはドットが2つ繋がれている)
    • 遠く離れたものに依存しているのは影響に気がつきにくくよくない
    • この法則に違反している場合インターフェースがよくないことが多い
  • オブジェクト指向オブジェクト指向間で交換されるメッセージによって定義される、そのメッセージは public interface にそって行われる

真ん中まで読んだ。
ためになる!のだが、静的型付と動的型付を比較し、最終的に「いいから動的型付は最高なんだよ」みたいに押し付けっぽい感じになっててかなり萎えた。
それぞれ良さはあるし、「私は動的型付けの方が良いと思っています」と主張を述べるだけのが良いのでは、この本の内容全部押し付けなのかなと思ってしまう。

  • ダックタイピング
    • DI みたいなもんっぽい、ダックタイピングの概念がしっくりこないまま読み進んでしまっているので結局なんだっけと言う感じになっている
    • 具象クラスに依存しないで抽象クラスに依存しよう
  • 動的型付けに慣れればその方がよくやすく、描きやすく、理解しやすいと思うでしょう
    • お前んなかではな?
  • 静的型付によて安全になると言う考えは幻想です
    • お前んなかではな?
  • 動的型付けはコンパイル時の型検査と言うコストが高い割にわずかな価値しか得られないが、動的型付けは莫大な効率性がある
    • まあビルド時間長いのはつらいな
  • 継承
    • 根本的には「メッセージの自動委譲」の仕組み

65%まできた。 Module と Class の違いが述べられていたがあまりよくわからなかった、つらい。

  • 抽象のルール
    • モデル化しているオブジェクトが一般-特殊の関係をしっかりと持っていること
    • 正しいコーディングテクニックを使っていること
  • Ruby は抽象クラスが存在しないので具象化できてしまうが、みんな良識があるから問題がない
    • いやいや、それで問題があるから静的型付によって強制力を効かせるんじゃないの
  • 抽象クラスを作るときには、具象クラスのメソッドを抽象クラスに移動させた方がいい
    • 抽象クラスから具象クラスに移動させるのはやめた方がいい
  • テンプレートメソッドで継承うまくやろう
  • モジュール
    • 様々なクラスのオブジェクトが、一箇所に定義されたコードを使って共通のロールを担うための完璧な方法
  • is-a
    • class
  • behaves-like-a
    • module

75%。コンポジションとかの話、正直よくわからなくて、もう一度この本を読み直した方が良さそう :cry:

  • 一部のサブクラスだけ使うようなメソッドをスーパークラスに置いちゃメッ!
  • スーパークラスとサブクラスは置換可能でなければならない
  • 継承する側で super を呼び出すのはやめて、フックメッセージを使おう
  • コンポジション
    • 組み合わされた全体が、異なる備品の就業以上となる方に、個別の部品を複雑な全体へと組み合わせる(コンポーズする)行為
    • 自転車にはパーツがある、 has-a の関係であると言うことらしい
    • 包含される側のオブジェクトは、包含する側のオブジェクトがなければ存在し得ないもの
      • 学部と言うのは大学がなくなれば存在しなくなるので、コンポジションと言える

87%まできた。明日読み終わる。
静的型付だったら問題にならないようなことをつらつら書いていて、なんでこの人静的型付ディスってたんだろうって感じがある。

  • 継承かコンポジションか迷ったらコンポジションにした方が良い
    • 継承よりも依存が少なくなるから
  • 継承の懸念2つ
    • 継承が適さない問題に対して、謝って継承を選択してしまう
    • 他のプログラマが予期せぬ目的で使うかもしれない
  • コンポジションをちゃんと使えれば、単純で抜き差し可能で、拡張性のたかく変更にも寛容なのができる
  • 継承は特殊化、 is-a関係に継承を使う
  • behaves-like-a関係にダックタイプを使う
  • has-a関係にコンポジション
  • テストダブルはロールの担い手を洋式化したインスタンスで、テストで飲み使われるもの
  • モックはダブルではない
    • モック、スタブ、テストダブルらへんが何をさすのかよくわからないのでちゃんと学びたい

読み終わった、良い本だった。

  • プライベートメソッドはなるべく書くな、書いたとしてもテストは書くな
  • 副作用のないメッセージはクエリメッセージ
  • テストを書く前にリファクタリングをしてより良い設計にしていこう
  • リスコフの原則に従っているか証明する簡単な方法は、契約を共有したテストを書き、全てのオブジェクトにそのテストをインクルードすること
  • スタブを用意するためにサブクラスを作るのは、リスコフの置換原則を破らない限り使える
    • サブクラスを作るときは、そのサブクラスが古くならないようにテストを書こう