TL;DR
- 多国通貨の計算を「式」のメタファーで捉え、同じインタフェースを持つ別のオブジェクトに計算を委譲する Imposter パターンで設計した。
- 為替の換算の責務は、
Expressionではなく専用のBankオブジェクトに持たせた。
内容
本章でやったこと
大きいテストを、進み具合が分かるように小さいテストに分けた。また、計算を表すためのメタファーを 2 つ検討した。
- 財布: さまざまな通貨や額面の紙幣が入った財布に見立てる
- 式:
$5 + 10CHFのような多通貨の計算を、数式の評価に見立てる
そして、メタファーを使って最初に書いたテスト(testSimpleAddition)を書き直した。
設計の方針が明確でない場合は、これまでの章と同じく、テスト、仮実装、リファクタリングのサイクルを回す。方針が明確な場合は、速度を上げてもよい(第9章 歩幅の調整)。
多国通貨の計算の難しさ
この問題の難しさは、複数の通貨を扱っていることを意識させないコードにしたい点にある。いちばん簡単なのは、すべてを基準通貨(たとえば US ドル)に換算することである。しかし、この方法では為替レートをうまく扱えない。そこで、為替レートを簡単に扱えて、計算が計算のように見えるようにしたい。これを解決するために、同じインタフェースを持つ新しいオブジェクト(Imposter)に計算処理を委譲する。
TDD はこのような優れた設計を保証するものではなく、結局はひらめきが必要である。ただし、テストとリファクタリングで手入れされたコードがあれば、ひらめくことも、ひらめいたことを実現することもしやすくなる。
Imposter パターン
Imposter(インポスター)は「なりすまし」という意味である。既存のオブジェクトが望む振る舞いをしないときに使う。同じインタフェースを持ちながら異なる実装を持つ別のオブジェクト(Imposter)を作り、そちらに処理を委譲する。
classDiagram class Client { +operation() } class Component { <<interface>> +operation() } class RealObject { +operation() } class Imposter { -realObjects List~Component~ +operation() } Client --> Component : uses Component <|.. RealObject : implements Component <|.. Imposter : implements Imposter o-- Component : contains
Null Object パターン(「何もしない」実装を差し込む)や Composite パターン(リストを単一のオブジェクトに見せる)も、Imposter の一種である。
本章では、Money.plus() の戻り値の型を Expression にし、Money が Expression を実装するようにした。
換算の責務を Bank に持たせる理由
換算の責務は Expression に持たせてもよいが、次の 2 つの理由から専用の Bank オブジェクトを作った。
Expressionは今回の実装の中心となるロジックを担いそうである。中心となるオブジェクトは責務を絞ったほうがよい。そうすることで、柔軟性、テスト容易性、再利用性が高まり、認知的負荷も減らせる- 今後、四則演算をはじめとするさまざまな演算の実装が控えているので、
Expressionが肥大化しそうである
キーワード
自分の理解
財布のメタファーは概念を理解するためのもので、実際の実装は式のメタファーで進む。
「複数の通貨を扱っていることを意識させないコードにしたい」という要件がどこから出てきたのか疑問だったが、「はじめに」にあった加重平均の話のあたりだろう。
基準通貨に換算すると為替レートをうまく扱えないのはなぜか。換算すると、いつのレートで換算したかという情報が失われるからだと理解した。たとえばレートを 2CHF = 1USD として $5 + 10CHF を計算すると、$5 + $5 = $10 になる。計算した時点で 10CHF という情報は消えるので、後でレートが 3CHF = 1USD に変わっても、$10 という値しか残っておらず換算し直せない。つまり評価が早すぎるのが問題なので、遅延評価にすればよいということだろう。
それにしても、Imposter に委譲するという設計は思いつけるだろうか。
Expression は今のところ空のインタフェースなので、後からここにメソッドを追加していくのだろう。
Expression の責務を絞る理由は、要するに単一責任の原則である。