オブジェクト指向設計ガイド
概要
オブジェクト指向とは、「依存関係を整理」すること
「依存関係を整理」するには、継承 ダックタイプ 委譲を使え
本書では、蒸気がどういったケースでどれが有効なのかを分かりやすく説明してくれてい
継承
ダックタイプ
委譲
本書のキーワード
単一責任
依存関係の管理
クラスではなくメッセージに基づく視点を持つ
--.icon
第1章 オブジェクト指向設計
オブジェクト指向とは、「依存関係を整理すること」
設計の目的は「あとにでも」設計をできるようにしておくこと
第一の目標は変更コストの削減
設計の知識を少し得たころこそ危険
執拗に設計するようになる
不適切な場所に原則を適用し存在しないところにパターンを見出す
アジャイルが成功するかどうかは、シンプルで適応性があり柔軟性があるコードにかかっている
オブジェクトは「データ」と「振る舞い」を持つ
設計理論を理解し正しい時期と正しい分量で適用する
第2章 単一責任のクラスを設計する
2.1 クラスに属するものを決める
メソッドをグループに分けクラスにまとめる
クラスはソフトウェアにおける仮想世界を定義する
この仮想世界が以降の工程に制約を課す
初期段階でのグループ分けは難しい
設計とはアプリケーションの可変性を保つためのものであり、完璧を目指すためのものではない
りく.iconつまり、クラス設計は、変更の必要が迫られた時に簡単に変更ができることを目指したもの。ってことか
変更がかんたんなようにコードを組成する
変更が簡単であるとはどういう状態か??
副作用をもたらさないこと
変更の要件が小さければ、コードの変更も小さい
既存のコードは簡単に再利用できる
もっとも簡単な変更は、コードを付け足すこと。ただし付け足すコードはそれ自体が変更が簡単であること
上記のように定義した場合、自身の書くコードは次のような性質を伴うべきである
見通しがいい: 変更するコードにおいても、そのコードにおいても、そのコードに依存する別の場所のコードにおいても 変更がもたらす影響が明白である
合理的: どんな変更であってもかかるコストは変更がもたらす利益にふさわしい
利用性が高い:新しい環境、予期していなかった環境でも再利用できる
規範的: コードに変更を加える人が、上記の品質を自然と保つようなコードになっている
2.2 単一の責任を持つクラスをつくる
単一責任とは?
一つのクラスや、一つのメソッド、一つのマイクロサービスの果たす役目が一つしかない状態のこと
「変更する理由が同じものは集める、変更する理由が違うものは分ける。」
変更する理由が2つ以上あるようではいけない
変更理由が複数あると、「変更する回数が増える」「複数の」
なぜ単一責任が重要なのか
変更のコストが増大してしまうから
クラス内の複数の責任は連動しているため「1つの責任への変更」があった時に「他の責任への変更」をもたらす
クラスが単一責任かどうかを見極める
二つ方法が紹介されている
①クラスに対して、そのクラスのメソッドを質問に言い換えてみる
②クラスを一文で説明してみる
設計を決定するときを見極める
決定は必要な時のみ、その時点で持っている情報を使ってする
developerは、設計の意図はコードに反映していると信じているから、いい加減な設計をすると後のdeveloperがそのいい加減な設計を引き継いでしまう
クラスが単一責任になっていないことに気づいた時、以下を問いかけると良い
「今日何もしないことによる、将来的なコストはどれだけなのか?」
2.3 変更を歓迎するコードを書く
データではなく,振る舞いに依存する
accessorを使って、インスタンス変数を隠蔽する
変わりにくいものに依存するべきであり、データは変わりやすいため、データを振る舞いによって隠蔽し、依存を振る舞いに移す
todo.iconStructは「意味と構造を分ける為」にあるのか、「クラスを分ける事なく属性を分ける為」」にあるのか整理がついていない
あらゆる箇所を単一責任にする
単一責任のメソッドがもたらす恩恵
隠蔽されていた性質を明らかにする(クラスの行おうとしている事が明確になる)
コメント書く必要無くなる
逆にコメントを書く必要が出てきたら、メソッドを他へ切り出すタイミング。
再利用を促進する
他のクラスへの移動が簡単(小さなメソッドは簡単に動かせる)
2.4 ついに,実際のWheelの完成
Structを独立したWheelクラスに変える
予め、Gearクラス内のWheelをStructによって切り出していたため、変更が容易になった
--.icon
第3章 依存関係を管理する
3.1 依存関係を理解する
依存関係を認識する
オブジェクトが次のものを知っている時、オブジェクトには依存関係があると言える
他のクラスの名前
self以外(他クラス)のどこかに送ろうとするメッセージの名前
↑ メッセージが要求する引数(あるメソッドにおいてどんな引数が必要なのか)
それらの引数の順番
オブジェクト間の結合(CBO:Coupling Between Objects)
大した事言ってない
クラス間の結合を無くしましょ〜 それぞれを独立させましょ〜ってこと
ほかの依存関係
「「何かを知るオブジェクト」を知るオブジェクト」を知るオブジェクト
メソッドチェーンの様な状態になるとやばい
3.2 疎結合なコードを書く
依存オブジェクトの注入
依存を隔離する
インスタンス変数の作成を分離する
あるクラス内で、他のクラスに参照する場合、インスタンスの生成の処理を専用のメソッドなどを作って隔離する
脆い外部メッセージを隔離する
wheel.diameterなどの外部クラスのメッセージの呼び出しを、専用のメソッドなどを作って隔離する
引数の順番への依存を取り除く
初期化の際の引数にハッシュを指定する
メリット:
keyを指定して取り出す為、順番を気にする必要が無くなる
keyを使う事でドキュメント代わりになり可読性が上がる
デフォルト値の設定
複数のパラメータを用いた初期化を隔離する
初期化時に多くのパラメータを受け取る場合、中間オブジェクトの様な(インターフェース的な?)ものを入れて変更に強くする
りく.icon他言語だとインターフェースという概念があるけどRubyだとやらないのか。何故やらないんだっけ?
3.3 依存方向の管理
依存関係の逆転
りく.iconこれメリットある??
依存方向の選択
変更されにくいものに依存せよ
変更のおきやすさを理解する
発展途上のフレームワークは代わりやすい事を意識する
りく.iconRailsのクラスで、どこが変わりやすいかとか意識する余地ありそう.....
具象と抽象を認識する
自分の作成したクラス間の依存を無くすということは、より抽象クラス(変更されにくいクラス)へ依存されることを意味する
りく.iconインターフェースの話に絡みそう?
他言語だと、抽象クラスではなくインターフェースに対して実装する?
動的型付けと静的型付けではデザインパターンが違う?確認.icon
大量に依存されたクラスを避ける
大量に依存されたクラスを変更した場合、その影響はそのクラスの依存度に比例して増える
問題となる依存関係を見つける
https://gyazo.com/8ca6ece09e96febbe9b5028c12201656
A〜Cの領域に集まっている事が望ましい
Bに関してはきにする必要ない
結論:依存を減らせ 依存するなら、自身より変更しないクラスに依存しろ
--.icon
第4章 柔軟なインターフェースをつくる
りく.iconクラス間の依存関係と、オブジェクト間で受け渡されるメッセージって何が違う??
4.1 インターフェースを理解する
4.2 インターフェースを定義する
パブリックインターフェース (ここでのインターフェースは「クラス内」にあるもの)
クラスの主要な責任を明らかにする
外部から実行される
気まぐれに変更されない
他者がそこに依存しても安定
テストで完全に文章化されている
プライベートインターフェース
クラス内のパプリックインターフェースで定義されているメソッド以外はすべてプラーベートインターフェース
実装の詳細に関わる
ほかのオブジェクトから送られてくることは想定されていない
どんな理由でも変更されえる
他者がそこに依存するのは危険
テストでは言及されないこともある
りく.iconRubyではクラス間のインターフェースがパブリックメソッドであるという事??
責任,依存関係,そしてインターフェース
単一責任とは、単一の目的のこと
従って、パブリックメソッドはその目的を果たす事をのべる契約書のようなもの
りく.iconプライベートメソッドの処理内容は、パブリックに依存しているため、プライベートメソッドがパブリックの目的に沿った処理である事が保証されている。そのため外部からはプライベートが知っていればプライベートを知る必要はないし、知るべきではない。て事????確認.icon
クラス間だけでなく、クラス内でも自身より変更しないクラスに依存しろの原則は当てはまる
プライベートよりもパブリックの方が安定しているべきであり、プライベートはパブリックに依存するべきである
ex)料理に例えるならば、
注文→料理が運ばれる→会計という流れは変わりにくい
一方、厨房内の細かい事は「変更しやすいし」、「お客から見えるべきものではないし」「お客がどうあるかに依存するべき」
4.3 パブリックインターフェースを見つける
アプリケーション例:自転車旅行会社
見当をつける
アプリケーションのユースケースを満足させるために必要な「オブジェクト」と「メッセージ」の両方についてまず見当をつける
オブジェクトに関しては、ドメインオブジェクトにすぐ気づける
大事なのはそのオブジェクト間で行われるメッセージ(やりとり)
メッセージに気づく多mにはシーケンス図が役立つ
シーケンス図を使う(UML)
UMLは振る舞い(メソッド, メッセージ)がある
クラスに基づく設計ではなく、メッセージに基づく設計をする事が大事
❎「このクラスが必要なのは知っているけれど、これは何をすべきなんだろう」
⭕️「このメッセージを送る必要があるけれど、誰が応答するべき何だろう」
❎オブジェクトがあるからメッセージを送る
⭕メッセージを送るためにオブジェクトは存在する
「どのように」を伝えるのではなく「何を」を頼む
クラスが目的を果たせていればおk
「どのように」に関しては知っておくべきではない
設計では、クラスがより少ない知識で目的を果たせる事を目指す
コンテキストの独立を模索する
コンテキストを「求める」という状態はコードの柔軟性とメンテナンス性を損なわせる
オブジェクト(Trip)を他のオブジェクト(bycycle)のコンテキストから完全に独立させる必要がある
依存オブジェクトの注入
ほかのオブジェクトを信頼する
依存オブジェクトの注入ことによって実現された
オブジェクトを見つけるためにメッセージを使う
メッセージを基本とするアプリケーションをつくる
4.4 一番良い面(インターフェース)を表に出すコードを書く
明示的なインターフェースをつくる
明示的にパブリックインターフェースだと特定できる
「どのように」よりも「何を」になっている
名前は、考えられる限り、変わり得ないものである
オプション引数としてハッシュを取る
ほかのパブリックインターフェースに敬意を払う
プライベートインターフェースに依存しない
パブリックインターフェースを信用しようってこと?
プライベートインターフェースに依存するときは,注意深く
どうしてもプライベートインターフェースに依存しちゃうときは、三章のテクニックを使って隔離をする
コンテキストを最小限にする
どのようにではなく、「何を」意識する?
どのように振舞うかを他オブジェクトにメッセージとして伝えてはいけない
4.5 デメテルの法則
デメテルを定義する
法則を違反することによる影響
違反を回避する
デメテルに耳を傾ける
--.icon
二章
単一責任のクラスを設計する
クラスに属するデータを決める
クラスの責任を単一にする
変更を歓迎するコードを実装する
題材
Gearクラス
chainring(チェーン)
cog(足で回すやつ)
ratio(チェーンとcogの比率からギアは計算される)
--.icon
副作用をもたらさないとは、その変更によって、入れられるデータの形式が限定されてしまったとか??りく.icon
めちゃくちゃピンとこないりく.icon
4章 旅行アプリの設計
DB
参加者
旅行
行程
自転車
整備士
りく.iconこいつらのことをドメインオブジェクトというらしい
りく.iconでメインオブジェクトとは大きくて目に見える現実世界のものを表し、かつ最終的にデータベースに表されるもの
Railsだと、DBに登録しようとすると、model(DBとやり取りするためのクラス)を作ることになるから勝手に登録されるよね。
要件
旅行を選ぶために、
適切な難易度の、
特定日付の、
自転車を借りられる
旅行の一覧を見たい
必要なクラス
現状
Gear(ギア)
ギアクラスのギアインチは車輪の大きさに依存している
wheel(車輪)
追加
難易度
日付
メモ
loyolaで変えられそうなところ
crawl_elementをseleniumから継承させる??