Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Application Design 勉強会 #3

Application Design 勉強会 #3

0e742980c48d689f31700b208f22fd5a?s=128

Kohei Kawaguchi

July 12, 2019
Tweet

Transcript

  1. "QQMJDBUJPO%FTJHO ษڧձ  ষ ஜ೾େֶ ৘ใϝσΟΞ૑੒ֶྨ೥ ઒ޱ ߤฏ 1

  2. Ϧείϑͷஔ׵ݪଇʢ-41ʣ 2

  3. Ϧείϑஔ׵ͷݪଇʢ-41ʣ 「派⽣型はその基本型と置換可能でなければならない.」 この原則に反すると,OCPにも反することとなりプログラムが脆くなる. - 関数fがある基本クラスAをその引数に取り - Aの派⽣クラスCを関数fに渡した際において異常を起こした…とする - この場合にクラスCはLSPに従わないという LSPはOCPを有効にする⼿段の1つであり

    あるモジュールに修正を施さずに拡張する⽅法として,基本型と派⽣型の置換性という形で表している 3
  4. Ϧείϑஔ׵ͷݪଇͷྫ • 「派⽣型はその基本型と置換可能でなければならない.」 • この原則に反すると,OCPにも反することとなりプログラムが脆くなる. 4 • UserやBookがEntityの代わりとして扱えないことに よって,LSP違反が起きている. •

    Entityがidentifierを持つことを担保すればこの問題 は起きない • また,Entityの種類が増えると分岐が増えるので OCP違反をしている. • LSP違反は⾃然にOCP違反を引き起こす.
  5. Ϧείϑஔ׵ͷݪଇʢ-41ʣ • 「派⽣型はその基本型と置換可能でなければならない.」 • この原則に反すると,OCPにも反することとなりプログラムが脆くなる. 5 • UserやBookがEntityの代わりとして扱えないことに よって,LSP違反が起きている. •

    Entityがidentifierを持つことを担保すればこの問題 は起きない • また,Entityの種類が増えると分岐が増えるので OCP違反をしている. • LSP違反は⾃然にOCP違反を引き起こす. この例では,単なる継承によって解決する. 継承によってLSPを満たせない場合はあるのか?
  6. ܧঝʹΑͬͯෳࡶԽ͞Εͨ-41ҧ൓ͷྫ • この⻑⽅形を表すRectangleViewに加えて正⽅形を表すSquareViewを追加する必要が出てきたケース. • 継承はIS-A(「である」)の関係であるといわれている. • 正⽅形は⻑⽅形であるのでSquareはRectanbleのサブクラスになりそうだ. 6

  7. ܧঝʹΑͬͯෳࡶԽ͞Εͨ-41ҧ൓ͷྫ • 本来⼀辺を設定できれば良いが,継承によって不要なメソッドを受け継いでしまった. • したがって,下記のように両辺の⻑さが等しいことを担保した. • 結果,Squareの性質を担保することが出来た! 7

  8. ܧঝʹΑͬͯෳࡶԽ͞Εͨ-41ҧ൓ͷྫ • 結果,Squareの性質を担保することが出来が…… • クライアントによっては下記のコードのような間違いが起きる. 8 関数fにSquareViewを与えたときに,assertでエラーが発⽣する. SquareViewはIS-Aの関係を満たしており正⽅形本来の性質も満たしている. じゃあ,このコードには正当性があり,間違っているのはクライアント?

  9. ܧঝʹΑͬͯෳࡶԽ͞Εͨ-41ҧ൓ͷྫ • 結果,Squareの性質を担保することが出来が…… • クライアントによっては下記のコードのような間違いが起きる. 9 関数fにSquareViewを与えたときに,assertでエラーが発⽣する. SquareViewはIS-Aの関係を満たしており正⽅形本来の性質も満たしている. じゃあ正当なのか? このコードが間違っているのだろうか?

    LSPに準拠するためにはIS-Aの関係では不⼗分. 「振る舞い」の同等性も必要! 誰もが合理的だと思う振る舞いをIS-Aに持たせる.
  10. ܖ໿ʹΑΔઃܭ • 合理的な仮定を明確にするテクニック. • あるメソッドについての事前条件と事後条件を決めておくことによって,そのメソッドを利⽤する⼈が 必要としている振る舞いを知る事ができる. • 事前条件: そのメソッドを実⾏する前に成⽴していなければいけない条件 •

    事後条件: メソッドが終了したときに成⽴していなければならない条件 • 基本クラスをインタフェースにした場合に,クライアントが理解しているのは基本クラスの事後条件と 事前条件のみ • 派⽣クラスの事前条件は基本クラスに課せられている事前条件より強くしてはいけない. • 派⽣クラスの事後条件は基本クラスの事後条件より弱くしてはいけない. Square::setWidthはRectanbleより事後条件が弱い.よって設計契約に反する. -> 振る舞いの同等性が担保されていない 10
  11. ܦݧଇʹґΔ͓खܰ-41ҧ൓νΣοΫ • Overrideによって関数の機能が劣化しているケース • 基本クラス以下のことしか出来ないのであれば置換は不可能. • 基本クラスでは投げない例外を派⽣クラスによって投げられているケース • 基本クラスの利⽤者がその例外を期待していないのでれば,置換不可能. •

    = 事前条件が強まってる?? 11
  12. ґଘੑٯసͷݪଇʢ%*1ʣ 12

  13. ґଘੑٯసͷݪଇʢ%*1ʣ 上位のモジュールは下位のモジュールに依存してはならない. どちらのモジュールも抽象に依存すべきである. 抽象は実装の詳細に依存してはならない.実装の詳細が抽象に依存すべきである. • 悪しき⼿続き型プログラミングでは⽅針が実装に依存する • 抽象と実装の詳細を完全に切り分けることによって変更に強くなる • 再利⽤性も⾼まる

    13
  14. %*1ͷϞνϕʔγϣϯ • アプリケーションの⽅針といったビジネスモデルを含む上位モジュールが下位のモジュールに依存する と,下位のモジュールの変更が上位モジュールに影響する. • それは本来逆であるべき.⾃分たちが使いたいのは上位のモジュールであるはず. 14 ґଘੑͷٯస 下位モジュールから上位モジュールへの

  15. %*1ʹΑͬͯӨڹ͕఻ൖ͠ʹ͍͘֊૚ߏ଄Λ࡞Δ • オブジェクト指向の⾔語では明確に定義付けられた階層を持つ • 上位のモジュールがインタフェースを持つことによって,下位のモジュールは上位のモジュールに依存 する. • インタフェースによって詳細の実装を隠蔽されているため上位のモジュールは下位のモジュールに依存 しない. 15

    本書p165より転載 本書p165より転載
  16. %*1ҧ൓ͷྫ 16 Button Lamp

  17. %*1ద߹ͷྫ 17 Button SwifthableServer Lamp

  18. %*1ద߹ͷྫ 18 Button SwifthableServer Lamp Buttonによって制御されるオブジェクトは全てButtonServerに準拠しなくてはいけない,というの は問題.Switchなどを操作することもある. ー> 命名を⼀般的にすることによって解決

  19. ந৅ʹґଘͤΑ • DIPは抽象に依存せよ,という経験則とも⾔える. • 具体的なクラスへのポインタやリファレンスを保持するような変数があってはならない • 具体的なクラスから派⽣するクラスがあってはならない • 基本クラスで実装されているメソッドを上書きするようなメソッドがあってはならない. 19

  20. Πϯλϑʔε෼཭ͷݪଇʢ*41ʣ 20

  21. ΠϯλϑΣʔε෼཭ͷݪଇ • 太ったクラスはそれを利⽤するクライアント同⼠で良くない問題を起こす. • 例えば,あるクライラントが依存先のクラスに変更を要求すると,別のクラスに変更の影響が伝搬する 可能性がある(OCP違反) • クライアントごと(もしくはクライアントのグループ)に必要なメソッドを切り出したインタフェース を作成することによって,この問題は解決できる. •

    結果,クライアントは⾃⾝が利⽤しないメソッドとの依存関係を切り捨てられる. 21 ΠϯλϑΣʔεͷ෼཭ クライアントに⾃⾝が利⽤しないメソッドへの依存を強要してはならない
  22. ΠϯλϑΣʔε෼཭ͷྫ • これらの定義を⽤いて⻑時間ドアが空いているとロックされるTimedDoorを実装する. 22

  23. ΠϯλϑΣʔε෼཭ͷྫ • よくある⼿法(らしい) • この⼿法ではTimerDoor⾃体がタイムアウトの通知を 受けられる利点がある⼀⽅でDoorがTimerDoorに依存 してしまう問題がある. • これはインタフェースを太らすインタフェースの汚染 である.

    • また,この⼿法はLSPに違反する. 23 • これらの定義を⽤いて⻑時間ドアが空いているとロックされるTimedDoorを実装する.
  24. ΠϯλϑΣʔε෼཭ͷྫ • よくある⼿法(らしい) • この⼿法ではTimerDoor⾃体がタイムアウトの通知を 受けられる利点がある⼀⽅でDoorがTimerDoorに依存 してしまう問題がある. • これはインタフェースを太らすインタフェースの汚染 である.

    • また,この⼿法はLSPに違反する. 24 • これらの定義を⽤いて⻑時間ドアが空いているとロックされるTimedDoorを実装する. インタフェースの分離=クライアントの分離 DoorとTimeClientは異なるクライアントにインタフェースを 提供する.ならばインタフェースを分離させるべき.
  25. ΠϯλϑΣʔε෼཭ͷྫ 25 Adapterパターンを⽤いた⽅法 多重継承を⽤いた⽅法

  26. Ҿ༻ • ロバート・C・マーチンほか.アジャイルソフトウェア開発の奥義 第 2版 オブジェクト指向開発の神髄と匠の技. SBクリエイティブ, 2008 26