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

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

zaru
April 29, 2018

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

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

zaru

April 29, 2018
Tweet

More Decks by zaru

Other Decks in Technology

Transcript

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

    View Slide

  2. @zaru
    @zaru
    @zaru
    @zaru
    @zaru
    @zaru
    2 / 22

    View Slide

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






    3 / 22

    View Slide

  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

    View Slide

  5. コードレビューしましょう
    コードレビューしましょう
    コードレビューしましょう
    コードレビューしましょう
    コードレビューしましょう
    コードレビューしましょう
    5 / 22

    View Slide

  6. 0
    が入ってきたらどうしよう
    0
    が入ってきたらどうしよう
    0
    が入ってきたらどうしよう
    0
    が入ってきたらどうしよう
    0
    が入ってきたらどうしよう
    0
    が入ってきたらどうしよう
    Numeric
    以外の値が入ってきたどうしよう
    Numeric
    以外の値が入ってきたどうしよう
    Numeric
    以外の値が入ってきたどうしよう
    Numeric
    以外の値が入ってきたどうしよう
    Numeric
    以外の値が入ってきたどうしよう
    Numeric
    以外の値が入ってきたどうしよう
    小数点はどうなってるの
    小数点はどうなってるの
    小数点はどうなってるの
    小数点はどうなってるの
    小数点はどうなってるの
    小数点はどうなってるの


















    6 / 22

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  12. 12 / 22

    View Slide

  13. 何が問題か
    何が問題か
    何が問題か
    何が問題か
    何が問題か
    何が問題か
    メソッドの
    メソッドの
    メソッドの
    メソッドの
    メソッドの
    メソッドの作り手
    作り手
    作り手
    作り手
    作り手
    作り手はどこまで
    はどこまで
    はどこまで
    はどこまで
    はどこまで
    はどこまで
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?
    メソッドを
    メソッドを
    メソッドを
    メソッドを
    メソッドを
    メソッドを使う側
    使う側
    使う側
    使う側
    使う側
    使う側はどこまで
    はどこまで
    はどこまで
    はどこまで
    はどこまで
    はどこまで
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?
    考慮すれば良いんだろうか?












    13 / 22

    View Slide

  14. 契約による設計
    契約による設計
    契約による設計
    契約による設計
    契約による設計
    契約による設計
    14 / 22

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  22. thanks
    thanks
    thanks
    thanks
    thanks
    thanks
    22 / 22

    View Slide