「テスト駆動開発」をPythonで書き直してみた 7
書籍「テスト駆動開発」をPythonで書き直したシリーズです。前回の記事はこちらです。
pyhaya.hatenablog.com
今回は、いよいよ多国通貨を扱うための準備に取り掛かります。
- 作者: Kent Beck,和田卓人
- 出版社/メーカー: オーム社
- 発売日: 2017/10/14
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
Bank(銀行)
多国通貨を扱うためには、為替レートを使って通貨を換算する必要があります。その役割を銀行クラスを作って任せます。どのような動作を期待するのか、テストを書いて明示します。
tests/test_money.py
import sys sys.path.append('../src') import unittest from money import Money class MoneyTest(unittest.TestCase): def testMultiplication(self): five = Money.dollar(5) self.assertEqual(Money.dollar(10), five * 2) self.assertEqual(Money.dollar(15), five * 3) def testEquality(self): self.assertNotEqual(Money.franc(5), Money.dollar(5)) def testSimpleAddition(self): bank = Bank() #銀行を用意 sum_ = Money.dollar(5) + Money.dollar(5) reduced = bank.reduce(sum_, "USD") #USDに換算する self.assertEqual(reduced, Money.dollar(10)) if __name__ == '__main__': unittest.main()
銀行を表すオブジェクトを用意しておいて、reduce
メソッドで通貨の両替を行います。
明白な実装
このテストを通す明白な実装はこのようになります。
src/bank.py
from money import Money class Bank: def reduce(self, source, to): return Money.dollar(10)
Moneyの実装を再考する
ここまで書いたら、一度Moneyの実装を考えてみます。今回、両替を銀行に委譲しました。なので、通貨同士の足し算は足した時点では通貨は決定していない状態です。ただ、足される2つの通貨を持っているだけの中間状態のようなものが必要です。いわば抽象的な通貨を表すためのクラスが必要です。
Sumクラスを実装する
このようなクラスとして、Sum
クラスを作ってみます。
src/sum.py
class Sum: def __init__(self, augent, addend): self.augent = augent self.addend = addend
src/money.py
from sum import Sum class Money: def __init__(self, amount, currency): self.amount = amount self.currency = currency def __eq__(self, other): return self.__dict__ == other.__dict__ def __add__(self, other): return Sum(self, other) def __mul__(self, multiplier): return Money(self.amount * multiplier, self.currency) @staticmethod def dollar(amount): return Money(amount, 'USD') @staticmethod def franc(amount): return Money(amount, 'CHF')
__add__
メソッドはSumクラスのインスタンスを返します。Sumクラスは初期化の際に被加算数(addend)と加算数(augent)を持ちます。
Bankクラスの本実装
ここまでくれば、ロジックが固まってきたのでBankクラスのreduceメソッドを書き下せます。source
引数はSumクラスのインスタンスを引数に持つので、
src/bank.py
from money import Money class Bank: def reduce(self, source, to): amount = source.augent.amount + source.addend.amount return Money(amount, to)
まとめ
今回は多国通貨を扱うための下準備を行いました。ここまでくるとだいぶロジックが複雑になってくるのでテストを書いてどのようなロジックにしたいのか明確にしておくことが重要です。