僕と契約して、メソッドになってよ!

1e68801494505bd0e0ce67e2e1398af3?s=47 zaru
April 29, 2018

 僕と契約して、メソッドになってよ!

TGIF 2018.04.27 LT スライド。
契約による設計(Contract by Design)から考えるメソッドの作り方

1e68801494505bd0e0ce67e2e1398af3?s=128

zaru

April 29, 2018
Tweet

Transcript

  1. 僕と契約して、メソッドになってよ! 僕と契約して、メソッドになってよ! 僕と契約して、メソッドになってよ! 僕と契約して、メソッドになってよ! 僕と契約して、メソッドになってよ! 僕と契約して、メソッドになってよ! 契約による設計から考えるメソッドの作り方 契約による設計から考えるメソッドの作り方 契約による設計から考えるメソッドの作り方 契約による設計から考えるメソッドの作り方

    契約による設計から考えるメソッドの作り方 契約による設計から考えるメソッドの作り方 1 / 22
  2. @zaru @zaru @zaru @zaru @zaru @zaru 2 / 22

  3. メソッドを作ってください メソッドを作ってください メソッドを作ってください メソッドを作ってください メソッドを作ってください メソッドを作ってください 2 つの引数を使って、割り算をした結果を返してください 2 つの引数を使って、割り算をした結果を返してください

    2 つの引数を使って、割り算をした結果を返してください 2 つの引数を使って、割り算をした結果を返してください 2 つの引数を使って、割り算をした結果を返してください 2 つの引数を使って、割り算をした結果を返してください       3 / 22
  4. こんな感じ? こんな感じ? こんな感じ? こんな感じ? こんな感じ? こんな感じ? def def div a

    div a, , b b a a / / b b end end div div 10 10, , 3 3 #=> 3 #=> 3 4 / 22
  5. コードレビューしましょう コードレビューしましょう コードレビューしましょう コードレビューしましょう コードレビューしましょう コードレビューしましょう 5 / 22

  6. 0 が入ってきたらどうしよう 0 が入ってきたらどうしよう 0 が入ってきたらどうしよう 0 が入ってきたらどうしよう 0 が入ってきたらどうしよう

    0 が入ってきたらどうしよう Numeric 以外の値が入ってきたどうしよう Numeric 以外の値が入ってきたどうしよう Numeric 以外の値が入ってきたどうしよう Numeric 以外の値が入ってきたどうしよう Numeric 以外の値が入ってきたどうしよう Numeric 以外の値が入ってきたどうしよう 小数点はどうなってるの 小数点はどうなってるの 小数点はどうなってるの 小数点はどうなってるの 小数点はどうなってるの 小数点はどうなってるの                   6 / 22
  7. 書き換えてみる 書き換えてみる 書き換えてみる 書き換えてみる 書き換えてみる 書き換えてみる 例えば、0 チェックやタイプチェックをしてあげる? 例えば、0 チェックやタイプチェックをしてあげる?

    例えば、0 チェックやタイプチェックをしてあげる? 例えば、0 チェックやタイプチェックをしてあげる? 例えば、0 チェックやタイプチェックをしてあげる? 例えば、0 チェックやタイプチェックをしてあげる? def def div a div a, , b b return return false false if if b b == == 0 0 return return false false unless unless [ [a a, , b b] ]. .all all? ?{ {| |v v| | v v. .is_a is_a? ? Numeric Numeric } } a a / / b b end end div div 10 10, , 0 0 #=> false #=> false div div '10' '10', , 3 3 #=> false #=> false 7 / 22
  8. 例えば、数値以外の値が入った時、よしなに変換をしてあげる? 例えば、数値以外の値が入った時、よしなに変換をしてあげる? 例えば、数値以外の値が入った時、よしなに変換をしてあげる? 例えば、数値以外の値が入った時、よしなに変換をしてあげる? 例えば、数値以外の値が入った時、よしなに変換をしてあげる? 例えば、数値以外の値が入った時、よしなに変換をしてあげる? def def div a

    div a, , b b a a, , b b = = [ [a a, , b b] ]. .map map( (& &method method( (:Float :Float) )) ) a a / / b b rescue rescue false false end end div div '10' '10', , 3 3 #=> 3.3333333333333335 #=> 3.3333333333333335 8 / 22
  9. 例えば、返す値の精度や丸めを調整してあげる? 例えば、返す値の精度や丸めを調整してあげる? 例えば、返す値の精度や丸めを調整してあげる? 例えば、返す値の精度や丸めを調整してあげる? 例えば、返す値の精度や丸めを調整してあげる? 例えば、返す値の精度や丸めを調整してあげる? def def div a

    div a, , b b a a, , b b = = [ [a a, , b b] ]. .map map( (& &method method( (:Float :Float) )) ) ( (a a / / b b) ). .round round( (3 3) ) end end div div 10 10, , 3 3 #=> 3.333 #=> 3.333 9 / 22
  10. まとめてみる まとめてみる まとめてみる まとめてみる まとめてみる まとめてみる def def div a

    div a, , b b a a, , b b = = [ [a a, , b b] ]. .map map( (& &method method( (:Float :Float) )) ) return return false false if if b b == == 0 0 return return false false unless unless [ [a a, , b b] ]. .all all? ?{ {| |v v| | v v. .is_a is_a? ? Numeric Numeric } } ( (a a / / b b) ). .round round( (3 3) ) rescue rescue false false end end 10 / 22
  11. くらべてみる くらべてみる くらべてみる くらべてみる くらべてみる くらべてみる def def div a

    div a, , b b a a / / b b end end def def div a div a, , b b a a, , b b = = [ [a a, , b b] ]. .map map( (& &method method( (:Float :Float) )) ) return return false false if if b b == == 0 0 return return false false unless unless [ [a a, , b b] ]. .all all? ?{ {| |v v| | v v. .is_a is_a? ? Numeric Numeric } } ( (a a / / b b) ). .round round( (3 3) ) rescue rescue false false end end 11 / 22
  12. 12 / 22

  13. 何が問題か 何が問題か 何が問題か 何が問題か 何が問題か 何が問題か メソッドの メソッドの メソッドの メソッドの

    メソッドの メソッドの作り手 作り手 作り手 作り手 作り手 作り手はどこまで はどこまで はどこまで はどこまで はどこまで はどこまで 考慮すれば良いんだろうか? 考慮すれば良いんだろうか? 考慮すれば良いんだろうか? 考慮すれば良いんだろうか? 考慮すれば良いんだろうか? 考慮すれば良いんだろうか? メソッドを メソッドを メソッドを メソッドを メソッドを メソッドを使う側 使う側 使う側 使う側 使う側 使う側はどこまで はどこまで はどこまで はどこまで はどこまで はどこまで 考慮すれば良いんだろうか? 考慮すれば良いんだろうか? 考慮すれば良いんだろうか? 考慮すれば良いんだろうか? 考慮すれば良いんだろうか? 考慮すれば良いんだろうか?             13 / 22
  14. 契約による設計 契約による設計 契約による設計 契約による設計 契約による設計 契約による設計 14 / 22

  15. 契約による設計 契約による設計 契約による設計 契約による設計 契約による設計 契約による設計 {P} A {Q} {P}

    A {Q} {P} A {Q} {P} A {Q} {P} A {Q} {P} A {Q} 任意の A の実行は、P の状態になったときに始まり、 任意の A の実行は、P の状態になったときに始まり、 任意の A の実行は、P の状態になったときに始まり、 任意の A の実行は、P の状態になったときに始まり、 任意の A の実行は、P の状態になったときに始まり、 任意の A の実行は、P の状態になったときに始まり、 Q の状態になったときに終了する。 Q の状態になったときに終了する。 Q の状態になったときに終了する。 Q の状態になったときに終了する。 Q の状態になったときに終了する。 Q の状態になったときに終了する。 by バートランド・メイヤー さん by バートランド・メイヤー さん by バートランド・メイヤー さん by バートランド・メイヤー さん by バートランド・メイヤー さん by バートランド・メイヤー さん 15 / 22
  16. 事前条件 事前条件 事前条件 事前条件 事前条件 事前条件 呼ぶ側で保証すべき性質 呼ぶ側で保証すべき性質 呼ぶ側で保証すべき性質 呼ぶ側で保証すべき性質

    呼ぶ側で保証すべき性質 呼ぶ側で保証すべき性質 事後条件 事後条件 事後条件 事後条件 事後条件 事後条件 終了時に呼ばれる側が保証すべき性質 終了時に呼ばれる側が保証すべき性質 終了時に呼ばれる側が保証すべき性質 終了時に呼ばれる側が保証すべき性質 終了時に呼ばれる側が保証すべき性質 終了時に呼ばれる側が保証すべき性質 不変条件 不変条件 不変条件 不変条件 不変条件 不変条件 すべての操作の開始時と終了時に保証されるべき、共通した性質 すべての操作の開始時と終了時に保証されるべき、共通した性質 すべての操作の開始時と終了時に保証されるべき、共通した性質 すべての操作の開始時と終了時に保証されるべき、共通した性質 すべての操作の開始時と終了時に保証されるべき、共通した性質 すべての操作の開始時と終了時に保証されるべき、共通した性質 16 / 22
  17. Eiffel で事前条件と事後条件 Eiffel で事前条件と事後条件 Eiffel で事前条件と事後条件 Eiffel で事前条件と事後条件 Eiffel で事前条件と事後条件

    Eiffel で事前条件と事後条件 class class STACK STACK [ [T T] ] feature feature is_empty is_empty : : BOOLEAN BOOLEAN; ; pop pop : : T T is is require require -- 事前条件 -- 事前条件 non_empty_stack non_empty_stack : : not not is_empty is_empty; ; do do -- 配列から1 個取り出す処理 -- 配列から1 個取り出す処理 ensure ensure -- 事後条件 -- 事後条件 one_less_count one_less_count : : count count = = old old count count - - 1 1 end end --pop --pop end end --STACK --STACK 17 / 22
  18. いいこと いいこと いいこと いいこと いいこと いいこと 明示的な条件を付けることで、作る側も使う側も安心できる 明示的な条件を付けることで、作る側も使う側も安心できる 明示的な条件を付けることで、作る側も使う側も安心できる 明示的な条件を付けることで、作る側も使う側も安心できる

    明示的な条件を付けることで、作る側も使う側も安心できる 明示的な条件を付けることで、作る側も使う側も安心できる 本来の責務に集中できるのでコードの見通しが良くなる 本来の責務に集中できるのでコードの見通しが良くなる 本来の責務に集中できるのでコードの見通しが良くなる 本来の責務に集中できるのでコードの見通しが良くなる 本来の責務に集中できるのでコードの見通しが良くなる 本来の責務に集中できるのでコードの見通しが良くなる 微妙な所 微妙な所 微妙な所 微妙な所 微妙な所 微妙な所 言語レベルでサポートしているのは Eiffel と D くらい 言語レベルでサポートしているのは Eiffel と D くらい 言語レベルでサポートしているのは Eiffel と D くらい 言語レベルでサポートしているのは Eiffel と D くらい 言語レベルでサポートしているのは Eiffel と D くらい 言語レベルでサポートしているのは Eiffel と D くらい Ruby の gem もあるけど実行時チェックなので… Ruby の gem もあるけど実行時チェックなので… Ruby の gem もあるけど実行時チェックなので… Ruby の gem もあるけど実行時チェックなので… Ruby の gem もあるけど実行時チェックなので… Ruby の gem もあるけど実行時チェックなので… https://github.com/egonSchiele/contracts.ruby https://github.com/egonSchiele/contracts.ruby https://github.com/egonSchiele/contracts.ruby https://github.com/egonSchiele/contracts.ruby https://github.com/egonSchiele/contracts.ruby https://github.com/egonSchiele/contracts.ruby                               18 / 22
  19. require require 'contracts' 'contracts' class class Example Example include include

    Contracts Contracts: :: :Core Core include include Contracts Contracts: :: :Builtin Builtin Contract Contract Num Num = => > Num Num def def double double( (x x) ) x x * * 2 2 end end end end puts puts Example Example. .new new. .double double( ("oops" "oops") ) 19 / 22
  20. ParamContractError ParamContractError: : Contract Contract violation violation for for argument

    argument 1 1 of of 1 1: : Expected Expected: : Num Num, , Actual Actual: : "oops" "oops" Value Value guarded guarded in in: : Example Example: :: :double double With With Contract Contract: : Num Num = => > Num Num At At: : ( (pry pry) ): :7 7 20 / 22
  21. Ruby に活かせる考え Ruby に活かせる考え Ruby に活かせる考え Ruby に活かせる考え Ruby に活かせる考え

    Ruby に活かせる考え YARD で引数の型や例外処理など挙動をちゃんと書こう YARD で引数の型や例外処理など挙動をちゃんと書こう YARD で引数の型や例外処理など挙動をちゃんと書こう YARD で引数の型や例外処理など挙動をちゃんと書こう YARD で引数の型や例外処理など挙動をちゃんと書こう YARD で引数の型や例外処理など挙動をちゃんと書こう IntelliJ なら YARD を読み取ってサジェストしてくれる IntelliJ なら YARD を読み取ってサジェストしてくれる IntelliJ なら YARD を読み取ってサジェストしてくれる IntelliJ なら YARD を読み取ってサジェストしてくれる IntelliJ なら YARD を読み取ってサジェストしてくれる IntelliJ なら YARD を読み取ってサジェストしてくれる メソッド内部で受け取った引数を メソッド内部で受け取った引数を メソッド内部で受け取った引数を メソッド内部で受け取った引数を メソッド内部で受け取った引数を メソッド内部で受け取った引数をよしなに よしなに よしなに よしなに よしなに よしなにするのは止めよう するのは止めよう するのは止めよう するのは止めよう するのは止めよう するのは止めよう 例外を投げるか false / nil を返すかは自由 例外を投げるか false / nil を返すかは自由 例外を投げるか false / nil を返すかは自由 例外を投げるか false / nil を返すかは自由 例外を投げるか false / nil を返すかは自由 例外を投げるか false / nil を返すかは自由 ただし、例外を内部で握りつぶすのはやめよう ただし、例外を内部で握りつぶすのはやめよう ただし、例外を内部で握りつぶすのはやめよう ただし、例外を内部で握りつぶすのはやめよう ただし、例外を内部で握りつぶすのはやめよう ただし、例外を内部で握りつぶすのはやめよう                               21 / 22
  22. thanks thanks thanks thanks thanks thanks 22 / 22