Python

FizzBuzz

仕様

  • 3で割り切れる場合はFizzを出力する

  • 5で割り切れる場合はBuzzを出力する

  • 両方で割り切れる場合はFizzBuzzを出力する

  • 上記以外の場合は数字を返す

  • 指定された回数だけ繰り返し実行する

設計

TODOリスト

  • [STRIKEOUT:Fizzを出力できるようにする]

  • [STRIKEOUT:Buzzを出力できるようにする]

  • [STRIKEOUT:FizzBuzzを出力できるようにする]

  • [STRIKEOUT:数字を出力できるようにする]

  • [STRIKEOUT:繰り返し実行できるようにする]

[1]:
class FizzBuzz():
    @staticmethod
    def generate(count):
        value = count

        if value % 3 == 0 and value % 5 == 0:
            value = 'FizzBuzz'
        elif value % 3 == 0:
            value = 'Fizz'
        elif value % 5 == 0:
            value = 'Buzz'

        return value

    @staticmethod
    def iterate(count):
        array = []

        for n in range(count):
            array.append(FizzBuzz.generate(n + 1))

        return array

import unittest


class FizzBuzzTest(unittest.TestCase):
    def test_3ならばFizzを返す(self):
        self.assertEqual(FizzBuzz.generate(3), 'Fizz')

    def test_6ならばFizzを返す(self):
        self.assertEqual(FizzBuzz.generate(6), 'Fizz')

    def test_5ならばBuzzを返す(self):
        self.assertEqual(FizzBuzz.generate(5), 'Buzz')

    def test_10ならばBuzzを返す(self):
        self.assertEqual(FizzBuzz.generate(10), 'Buzz')

    def test_50ならばBuzzを返す(self):
        self.assertEqual(FizzBuzz.generate(5), 'Buzz')

    def test_15ならばFizzBuzzを返す(self):
        self.assertEqual(FizzBuzz.generate(15), 'FizzBuzz')

    def test_30ならばFizzBuzzを返す(self):
        self.assertEqual(FizzBuzz.generate(30), 'FizzBuzz')

    def test_1ならば1を返す(self):
        self.assertEqual(FizzBuzz.generate(1), 1)

    def test_101ならば101を返す(self):
        self.assertEqual(FizzBuzz.generate(101), 101)

    def test_5回実行されたならば配列を返す(self):
        self.assertEqual(FizzBuzz.iterate(5), [1, 2, 'Fizz', 4, 'Buzz'])

    def test_10回実行されたならば配列を返す(self):
        self.assertEqual(FizzBuzz.iterate(10), [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz'])


if __name__ == '__main__':
    unittest.main(argv=['none'], exit=False)
.........

開発

イテレーション1

仕様を満たすアプリケーションをできるだけ早く試作するためにテストファーストプログラミングを実施する。実施にあたってまずTODOリストを作成する。

TODOリストを作成したらインクリメンタルな設計を実施する。実施するにあたってまず単一責任の原則に従い関心事を表すクラスを定義する。ここではFizzBuzzそのものを関心事として扱うことにする。

テストファーストでFizzBuzzとテストのクラスの定義式を作成する。

クラスを定義したのでテストメソッドを作成に入る。まずTODOリストを元にアウトラインを作成する。

アウトラインをを作成したら一歩を示すテストに取り掛かる、TODOリストから失敗するはじめのテストを作成する。テストコードはアサートファーストでレッドになることを確認したら仮実装でグリーンにする。一つのプログラムには一つのことをうまくやらせることを心がける。アサーションは期待値を先に書く。

グリーンの状態になったら仮実装を経て本実装へ入る。明白な実装を行って最初のTODOリストを片付ける。条件分岐演算子を使って本実装を行う。YAGNIに従い今必要な機能だけを実装する。

引き続きTODOリストの2つ目に取り掛かる。はじめのテストを失敗させる。失敗することを確認したら明白な実装によりテスト結果をグリーンにする。最初のコードに追記するだけでは意図した結果にならなかったのでを変更して結果をグリーンにした。TODOリストの2つ目を完了にする。

続いて、TODOリストの3つ目に着手する。今回は複数の条件なので新たな演算子を使って実装する。実装に際してロジックの順番の間違いと判断条件の間違いとコピペによる間違いを犯していたが全てテストで気づくことができた。

TODOリストの割り算機能の最後のタスクに取り掛かる。今回のテストは前回のテストの派生系なので三角測量で実装する。まずは失敗するテストを追加する。明白な実装によりテストをグリーンにしてTODOリストの割り算機能のタスクを片付ける。テストのアウトラインがなくても意図は読み取れるようになったのでコメントは削除する。

続いて、TODOリストの残りの実行機能に取り組む。まずインクリメンタルな設計によりクラスのインタフェースを設計する。一つのプログラムには一つのことをうまくやらせることを意識する。

実行機能はまだ正しい実装が見えてこないので仮実装を経て本実装への前に三角測量を実施する。ローカル変数を初期化して繰り返しの制御構文で実行された最後の結果を返す実装をしたがテストにはまだパスしない。テストから演算子の間違いを発見しさらに仮実装の間違いがあることが判明したのでテストを修正してグリーンにする。

仕様を確認したところ間違いがあったので修正してテストも修正してグリーンになることを確認した。また、仕様に明記されていない条件が判明したのでTODOリストに追加する。

最初のイテレーションを完了させるにあたってふりかえりを実施する。

ふりかえり

Keep

  • 一つのプログラムには一つのことをうまくやらせる基本定理に従い単一責任の原則に沿ったクラス設計をを実施した。

  • できるだけ早く試作するためにインクリメンタルな設計テストファーストプログラミングと取り組むためのTODOリストを作成した。

  • 仕様に明記されていない項目をTODOリストに反映した。

  • TODOリストに従いテストファースト一歩を示すテストを作成した

  • まずはじめのテストアサートファーストで取り掛かりベイビーステップですすめて行った。

  • 何を書くべきかわかっているときは、明白な実装を行い、わからないときには仮実装を行い、まだ正しい実装が見えてこないときには三角測量を行いながら仮実装を経て本実装へと進めた。

Problem

  • Pythonのモジュール読み込みの仕組みにはまる

Try

  • Unittestを理解する