Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第12章 設計とメタファー #16

Merged
merged 11 commits into from
Nov 9, 2017
Merged

第12章 設計とメタファー #16

merged 11 commits into from
Nov 9, 2017

Conversation

at-grandpa
Copy link
Owner

TODO

  • $5 + 10 CHF = $10(レートが2:1に場合)
  • $5 * 2 = $10
  • amountをprivateにする
  • Dollarの副作用どうする?
  • Moneyの丸め処理をどうする?
  • equals
  • hashCode
  • nullとの等価性比較
  • 他のオブジェクトとの等価性比較
  • 5CHF*2=10CHF
  • DollarとFrancの重複
  • equalsの一般化
  • timesの一般化
  • FrancとDollarを比較する
  • 通貨の概念
  • testFrancMultiplicationを削除する?

@at-grandpa
Copy link
Owner Author

未解決項目を新規リストに転機していく。

@at-grandpa
Copy link
Owner Author

at-grandpa commented Nov 8, 2017

TODO

  • $5+10CHF=$10(レートが2:1の場合)
  • $5+$5=$10

@at-grandpa
Copy link
Owner Author

シンプルな足し算のテストを書く。

@at-grandpa
Copy link
Owner Author

落ちる。

 crystal spec' <

Error in line 1: while requiring "./spec/money_package_spec.cr"

in spec/money_package_spec.cr:28: undefined method 'plus' for MoneyPackage::Money

      sum = Money.dollar(5).plus(Money.dollar(5))
                            ^~~~

Rerun with --error-trace to show a complete error trace.

@at-grandpa
Copy link
Owner Author

Money.dollar(10)を返すテストを書くこともできるが、一度sumに足したほうが実装は明白。

@at-grandpa
Copy link
Owner Author

通貨間の為替レートを簡単に扱えて、かつ計算は計算のように 見える方法を模索したい。

これを目指していく。

@at-grandpa
Copy link
Owner Author

オブジェクトたちに頼ろう。あるオブジェクトが望むように振る舞えないのな らば、同じ外部プロトコル(メソッド群)を備える新たなオブジェクト(Imposter
(p. 247)と呼ばれる)を実装し、仕事をさせればよい。

一旦新しく作る作戦かな。

@at-grandpa
Copy link
Owner Author

読者の皆さんには、手品のように見えるかもしれない。ここで Imposter パター
ンのような仕組みを使えばいいのだと、どうやって気づけばよいのだろうか

そうそう、この「こう気づく」ってのはどうすればいいのか。やっぱり経験値なのかなぁ。

@at-grandpa
Copy link
Owner Author

設計がひらめく瞬間に法則性はない

はい。

@at-grandpa
Copy link
Owner Author

at-grandpa commented Nov 9, 2017

Imposter(なりすまし)パターンを用いていく。Expressionクラスを定義する。

それらの経緯は以下。

  • 複数通貨を計算したい
  • 為替レートも扱いたい
  • 計算は計算のように見えるようにしたい
  • Moneyのように振る舞うが、二つのMoneyの合計を表現するオブジェクトを作成し、そのオブジェクトに「Moneyだけでは扱えなかった為替レートの機能」を付加する
    • これは、「同じように振る舞うが、異なる仕事をさせたい」という要望であり、Imposterパターンの出番になっている
  • 「式(Expression)」というメタファーを使っていく
  • MoneyはExpressionを構成する最小単位
  • さまざまな計算処理の結果はExpressionオブジェクトになる
  • 最終的にExpressionオブジェクトは、為替レートによって、特定のMoneyに換算できる

こういう設計にする。


設計判断をしている。

  • Expressionは今やる中核。これは責務を一つにしたい。
  • Expressionを肥大化させたくない。

設計を決定づけるものではなく、方向性を決めるには十分。

@at-grandpa
Copy link
Owner Author

Expressionをinterfaceとして定義する。

@at-grandpa
Copy link
Owner Author

コンパイルは落ちる。

 crystal spec' <

Error in line 1: while requiring "./spec/money_package_spec.cr"

in spec/money_package_spec.cr:29: instantiating 'MoneyPackage::Money#plus(MoneyPackage::Money)'

      sum : Expression = five.plus(five)
                              ^~~~

in src/money_package/money.cr:18: type must be MoneyPackage::Expression, not MoneyPackage::Money

    def plus(addend : self) : Expression
        ^~~~

@at-grandpa
Copy link
Owner Author

Expressionをincludeした。

interface的な役割をもたせたいなら、abstractメソッドをExpressionに持たせればいい。

こんな感じ。

module MoneyPackage
  module Expression
    abstract def hoge
  end
end

こうすれば、

 crystal spec' <

Error in src/money_package/expression.cr:3: abstract `def MoneyPackage::Expression#hoge()` must be implemented by MoneyPackage::Money

    abstract def hoge
                 ^~~~

といった感じでコンパイル時に落ちてくれる。

良い。

@at-grandpa
Copy link
Owner Author

コンパイルが通って、テストが落ちる。

 crystal spec' <

...F

Failures:

  1) MoneyPackage testSimpleAddition() 足し算を計算できること
     Failure/Error: reduced.should eq Money.dollar(10)

       Expected: #<MoneyPackage::Money:0x10ae28b80 @amount=10, @currency="USD">
            got: nil

     # spec/money_package_spec.cr:32

Finished in 1.03 milliseconds
4 examples, 1 failures, 0 errors, 0 pending

Failed examples:

crystal spec spec/money_package_spec.cr:27 # MoneyPackage testSimpleAddition() 足し算を計算できること

@at-grandpa
Copy link
Owner Author

振り返り。

  • 大きいテスト $5+10CHF を分解して、進み具合のわかる小さいテスト$5+$5を作成した
  • これから行う計算のためのメタファーについて深く考えた
  • 前章で書いたテストを、新しいメタファーを使って書き直した
  • テストがコンパイルできるところまで早足で進んだ
  • テストを通した
  • 本当の実装を導くためのリファクタリングを楽しみつつ、少し不安も感じている

@at-grandpa
Copy link
Owner Author

「どうやって設計するのかな」と疑問だったが、なんとなく妥当な理由が書いてあってokだと思う。

@at-grandpa
Copy link
Owner Author

12章終了。

@at-grandpa at-grandpa merged commit d77ba26 into master Nov 9, 2017
@at-grandpa
Copy link
Owner Author

TODO

  • $5+10CHF=$10(レートが2:1の場合)
  • $5+$5=$10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants