オブジェクト指向 パラメータ多相・実装継承 / OOP3

E37b4344ef4bfd0fc4826c04971e54fb?s=47 nrs
October 28, 2018

オブジェクト指向 パラメータ多相・実装継承 / OOP3

勉強会での登壇用資料です。
オブジェクト指向のパラメータ多相と実装継承 についての解説です。
https://nrslib.com

E37b4344ef4bfd0fc4826c04971e54fb?s=128

nrs

October 28, 2018
Tweet

Transcript

  1. オブジェクト指向入門 パラメータ多相 実装継承 nrs @nrslib

  2. パラメータ多相

  3. パラメータ = ?

  4. ArrayList

  5. ArrayList キャストすれば計算はできる

  6. ArrayList 実行時エラー もし文字列を代入したら?

  7. ArrayList 実行時エラー

  8. ArrayList

  9. ArrayList 型を意識する必要が出てしまう

  10. 問題点 ? =

  11. 問題点 キャスト =

  12. キャストをするということは 問題点 キャスト =

  13. キャストをするということは list 変数には 数字しか入らないという 暗黙的なルールがある 問題点 キャスト =

  14. キャストをするということは 暗黙的なルール = ランタイムエラーの原因 list 変数には 数字しか入らないという 暗黙的なルールがある 問題点 キャスト

  15. どうすれば解決できるのか

  16. どうすれば解決できるのか ランタイムエラーではなく コンパイルエラーにする

  17. どうすれば解決できるのか ランタイムエラーではなく コンパイルエラーにする ジェネリクス(総称型)

  18. List<T> int 型の List と宣言

  19. List<T> int 型の List と宣言 int 型は要素として追加できる

  20. List<T> int 型の List と宣言 int 型は要素として追加できる int 型の List

    の要素なので int 型 (キャスト不要!)
  21. List<T> int 型以外の要素追加は コンパイルエラー

  22. パラメータ = ?

  23. パラメータ = ?

  24. パラメータ = 型

  25. パラメータ = 型 パラメータ多相は型情報を抽象化する 型を抽象化して捉えることで 特定の型に捉われない実装を行える

  26. エクササイズ

  27. お題はエラーコード

  28. エラーコード

  29. エラーコード エラーコードってどう扱っていますか 形式は数字ですか? 文字列ですか? 粒度はどれくらいですか?

  30. エラーコード 形式は列挙体で 粒度はユースケース毎 にしようと思いました

  31. エラーコード

  32. エラーコード

  33. エラーコード ユースケース毎に起こりうるエラーがわかる

  34. エラーコード ユースケース毎に起こりうるエラーがわかる -> エラーコードからの該当箇所特定が容易

  35. レスポンス どんなクラスを作る?

  36. レスポンス

  37. レスポンス

  38. レスポンス

  39. レスポンス

  40. レスポンス 似たようなコードが多いので スーパークラスを作ります

  41. スーパークラス

  42. スーパークラス

  43. スーパークラス

  44. スーパークラス

  45. 使ってみよう

  46. 使ってみよう

  47. 使ってみよう

  48. 使ってみよう もしエラーコードで メッセージを出し分けしたかったら?

  49. エラーコードでメッセージを出し分け

  50. エラーコードでメッセージを出し分け イマイチポイント① キャスト

  51. エラーコードでメッセージを出し分け キャスト 実際のクラスを知っている = イマイチポイント① キャスト

  52. エラーコードでメッセージを出し分け イマイチポイント② 他のレスポンスでも 似たようなロジックを 書くことになりそう

  53. 目標はこんな感じ

  54. 実装してみよう 目標はこんな感じ

  55. まずはキャスト撲滅

  56. object を使っているところを

  57. ジェネリクスに

  58. ジェネリクスに

  59. レスポンスが変更されます

  60. レスポンスが変更されます

  61. キャスト消失

  62. キャスト消失

  63. 次はここ

  64. 一旦の目標

  65. エラーメッセージに変換するモジュール

  66. エラーメッセージに変換するモジュール Q. 他のエラーコードにも対応させたら?

  67. None
  68. イマイチポイント 型確認

  69. イマイチポイント キャスト イマイチポイント 型確認

  70. イマイチポイント キャスト イマイチポイント 型確認 イマイチポイント どんどん増える

  71. イマイチポイント キャスト イマイチポイント 型確認 イマイチポイント どんどん増える 総評 イマイチ

  72. そもそもエラーコードに対応する メッセージはどこに書くべきか

  73. エラーコードに書きたい

  74. エラーコードに書きたい ※多言語化するときはメッセージIDです

  75. エラーコードからエラーメッセージを設定する

  76. エラーコードからエラーメッセージを設定する

  77. エラーコードからエラーメッセージを取得する

  78. エラーコードからエラーメッセージを取得する {UserPostErrorCode.IllegalCharactorOnUserName, ErrorMessageAttribute(“ユーザ名に利用できない文字が存在しています”)}, {UserPostErrorCode.DuplicatedUserName, ErrorMessageAttribute(“ユーザー名が重複しています”)} という連想配列を作ってる

  79. モジュール化してみる

  80. モジュール化してみる

  81. モジュール化してみる これを使うとこうなる

  82. モジュール化してみる オートボクシング発生

  83. オートボクシングを防ぐには

  84. オートボクシングを防ぐには object を使わなければいい

  85. オートボクシングを防ぐには object を使わなければいい ジェネリクス

  86. None
  87. めでたしめでたし…

  88. と思いきや

  89. 使ってみると

  90. 使ってみると 型を書かなくてはいけない

  91. 使ってみると 型を書かなくてはいけない 型は大事だけど 型を意識する役目はコンパイラ

  92. 型を書かなくてもいいように 改良しよう

  93. まずは少しでもタイプセーフにするために マークアップインターフェースを用意 (object で扱えばいいので必須ではないです)

  94. メッセージ取得部分をクラスに

  95. 作ったクラスを利用する

  96. 出来上がった最終形

  97. 型を意識しないようにすると

  98. 最終形は?

  99. 拡張メソッドで

  100. 拡張メソッドで 拡張メソッドは乱用不可 継承でも実現できる 拡張メソッドを採用する理由は要検討

  101. 拡張メソッドで 拡張メソッドは乱用不可 継承でも実現できる 拡張メソッドを採用する理由は要検討 今回の採用理由は メッセージ処理はプレゼンテーション層の処理 Response というオブジェクトが担保すべきでない

  102. ジェネリクスの制約について

  103. 制約 型パラメータに条件を付ける機能

  104. 制約

  105. 制約 Response のメソッド 呼びたいけど

  106. 制約 Response のメソッド 呼びたいけど 型がわからない

  107. 制約 TResponse は Response か Response のサブタイプである と定義

  108. 制約 TResponse は Response か Response のサブタイプである と定義 ToErrorMessage() が呼べる

  109. 共変 / 反変

  110. 共変 / 反変 頭がこんがらがるので省略

  111. 共変 / 反変 頭がこんがらがるので省略 in 修飾子 out 修飾子 こういうことを可能にします

  112. ジェネリクスまとめ 本来やりたいことは object で実現できる でも型の恩恵は受けたいので型を抽象化した

  113. 実装継承の話をします

  114. 継承は

  115. 継承は ポリモーフィズムを 実現するための機能である

  116. 継承は ポリモーフィズムを 実現するための機能である

  117. 継承は コードの共通化を 実現するための機能である

  118. なぜこれを強調するのか

  119. 継承はとても難しいから なぜこれを強調するのか

  120. 継承の難しさ

  121. 継承の難しさ

  122. 継承の難しさ

  123. 継承の難しさ

  124. 継承の難しさ 継承構造は利用者次第 作成者が関与できない

  125. None
  126. None
  127. None
  128. メソッドを override するかしないか

  129. メソッドを override するかしないか これは形を変えた条件分岐

  130. 合わさるとやばい

  131. サブタイプのための継承が行われる

  132. サブタイプのための継承が行われる

  133. サブタイプのための継承が行われる

  134. サブタイプのための継承が行われる スーパークラスで 利用されていない

  135. サブタイプのための継承が行われる スーパークラスで 利用されていない 共通化すべき処理が 発見されたとき もう継承できない

  136. こんな感じがおすすめ

  137. こんな感じがおすすめ

  138. こんな感じがおすすめ 共通化する目的は 改修する時に変更箇所を集約することが目的 書く時に楽をするためだけの継承はしてはいけない

  139. もはやそれは別物だよ

  140. もはやそれは別物だよ

  141. もはやそれは別物だよ is-a 関係の難しさ

  142. まとめ 結果として出来上がる継承構造の形成に関して スーパークラスの製作者が制御する仕組みがない オーバーライドできるメソッドの数により複雑さが増す サブタイプのための継承が可能だが 本来の目的から反する クラスハックのための継承が行われることがあり 驚き最小の原則に反する

  143. 継承の怖さについて学んだところで 継承をしてみよう

  144. None
  145. まずは 重複していないところを チェック

  146. None
  147. None
  148. None
  149. 継承方針を決める

  150. 継承方針を決める スーパークラスは何か

  151. 継承方針を決める スーパークラスは何か 「ファイルを出力するコマンド」

  152. None
  153. この部分は virtual メソッドで用意する?

  154. もし用意したらどうなる?

  155. たぶんこうなる

  156. この部分は virtual メソッドで用意する?

  157. この部分は virtual メソッドで用意する? → No! 振る舞いがないのでパラメータで十分

  158. None
  159. この部分は virtual メソッドで用意する?

  160. この部分は virtual メソッドで用意する? → Yes ! 振る舞いがあるので override 必要

  161. スーパークラス

  162. スーパークラス パラメータで受け取り

  163. スーパークラス 振る舞いは変更可能

  164. 継承すると

  165. 継承すると

  166. 継承すると

  167. 継承すると 差分に集中できる

  168. よりよいスーパークラス

  169. よりよいスーパークラス 現在のインターフェース

  170. よりよいスーパークラス 必ず実装してほしい 現在のインターフェース

  171. よりよいスーパークラス

  172. よりよいスーパークラス

  173. よりよいスーパークラス 仮実装不要

  174. よりよいスーパークラス インターフェース

  175. よりよいスーパークラス インターフェース

  176. よりよいスーパークラス インターフェース 継承前提のクラスだと わかる

  177. よりよいスーパークラス インターフェース 実装しないと コンパイルエラー 継承前提のクラスだと わかる

  178. 実は継承を使わずに 同様のことを実現ができます

  179. None
  180. 関数を引数で受け取る

  181. 利用イメージ

  182. 利用イメージ

  183. 継承するかしないか

  184. 継承するかしないか 継承 Ver. 関数 Ver.

  185. 継承するかしないか 継承 Ver. 関数 Ver. 重複!

  186. 継承するかしないか 継承したクラス自体を 再利用するとき継承する 継承 Ver. 関数 Ver.

  187. 関数を受け取るクラスの継承

  188. 関数を受け取るクラスの継承

  189. override されるメソッドについて 簡単なガイドライン

  190. 範囲を小さく 初期化処理に サブクラスで追加処理したい

  191. 範囲を小さく

  192. 範囲を小さく

  193. 範囲を小さく super.Initialize()を 呼ばないと スーパークラスの初期化が発生しない

  194. 範囲を小さく super.Initialize()を 呼ばないと スーパークラスの初期化が発生しない =スーパークラスの破壊が可能

  195. 範囲を小さく

  196. スーパークラスの 初期化を担保している 範囲を小さく

  197. ネーミング

  198. ネーミング 初期化処理の前 初期化処理の後

  199. ネーミング 初期化処理の前 初期化処理の後 Hook するメソッドが処理の最初か最後かは 重要な要素であったりするので 現在進行形と過去形で使い分ける

  200. 実装継承まとめ 処理の共通化が目的 共通化することで改修する時の変更箇所を集約する 振る舞いがあるかどうかは継承をするかの大事な判断基準

  201. Auther nrs HomePage https://nrslib.com Twitter @nrslib