pyhaya’s diary

機械学習系の記事をメインで書きます

「テスト駆動開発」をPythonで書き直してみた 5

書籍「テスト駆動開発」をPythonで書き直してみたシリーズの5です。前回は、DollarクラスとFrancクラスの親クラスとしてMoneyクラスを作り、重複したコードを親クラスへ引き上げました。
pyhaya.hatenablog.com

テスト駆動開発

テスト駆動開発

DollarクラスとFrancクラスを消す

テストでDollar、Francを使わないようにする

DollarクラスとFrancクラスを消すために、まずはテストコードからこれらのクラスを使っている部分をなくしていきます。

tests/test_money

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.times(2))
        self.assertEqual(Money.dollar(15), five.times(3))

    def testFrancMultiplication(self):
        five = Money.franc(5)
        self.assertEqual(Money.franc(10), five.times(2))
        self.assertEqual(Money.franc(15), five.times(3))

    def testEquality(self):
        self.assertNotEqual(Money.franc(5), Money.dollar(5))

if __name__ == '__main__':
    unittest.main()

Moneyクラスを経由してDollarクラスとFrancクラスを読み込みます。

Moneyクラスを書き直す

ではテストが通るようにMoneyクラスを書き換えます。だんだんインポートが面倒になってきたのでDollarクラスとFrancクラスをmoney.pyに移してきます。

src/money.py

class Money:
    def __init__(self, amount, currency):
        self.amount = amount
        self.currency = currency

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    @staticmethod
    def dollar(amount):
        return Dollar(amount)

    @staticmethod
    def franc(amount):
        return Franc(amount)


class Franc(Money):
    def __init__(self, amount, currency='CHF'):
        super().__init__(amount, currency)

    def times(self, multiplier):
        return Franc(self.amount * multiplier)


class Dollar(Money):
    def __init__(self, amount, currency='USD'):
        super().__init__(amount, currency)

    def times(self, multiplier):
        return Dollar(self.amount * multiplier)         

Moneyクラスに吸収する

ここまでくれば、FrancクラスとDollarクラスの削除まではもう一歩です。これらのクラスの違いはcurrencyフィールドの違いだけなので、次のようにしてやれば統合できそうです。

class Money:
    def __init__(self, amount, currency):
        self.amount = amount
        self.currency = currency    #追加

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    @staticmethod
    def dollar(amount):
        return Money(amount, 'USD')

    @staticmethod
    def franc(amount):
        return Money(amount, 'CHF')

    def times(self, multiplier):
        return Money(self.amount * multiplier, self.currency)

これで、DollarとFrancクラスの統合は完了です。最後にテストを走らせて成功するのを確認します。