Skip to content

第 3 章: 明白な実装とリファクタリング

3.1 はじめに

前章では、3・5・15 の各ケースを仮実装で通しました。この章では、仮実装を汎用的なロジックに リファクタリング し、「明白な実装」に変換します。さらに、1 から 100 までの配列を返す機能とプリント機能を追加して、FizzBuzz プログラムを完成させます。

3.2 明白な実装

明白な実装

シンプルな操作をどうやって実装すればよいか——そのまま実装しよう。

— テスト駆動開発

Red: 一般化のためのテスト

6(3 の倍数だが 3 ではない)を渡すテストを追加して、仮実装の限界を明らかにします。

public function test_6を渡したら文字列Fizzを返す(): void
{
    $fizzbuzz = new FizzBuzz();
    $this->assertSame('Fizz', $fizzbuzz->generate(6));
}

テストを実行すると失敗します(期待値は "Fizz"、実際は "6")。

Refactor: 倍数チェックの一般化

特定の数値へのハードコーディングを、倍数チェックのルールに変更します。

public function generate(int $number): string
{
    if ($number % 15 === 0) {
        return 'FizzBuzz';
    }
    if ($number % 3 === 0) {
        return 'Fizz';
    }
    if ($number % 5 === 0) {
        return 'Buzz';
    }
    return (string) $number;
}

PHP の % 演算子(剰余演算子)は Go や Java と同じ挙動です。

$ vendor/bin/phpunit
OK (6 tests, 6 assertions)

さらにテストを追加して確認します。

public function test_10を渡したら文字列Buzzを返す(): void
{
    $fizzbuzz = new FizzBuzz();
    $this->assertSame('Buzz', $fizzbuzz->generate(10));
}

public function test_30を渡したら文字列FizzBuzzを返す(): void
{
    $fizzbuzz = new FizzBuzz();
    $this->assertSame('FizzBuzz', $fizzbuzz->generate(30));
}
$ vendor/bin/phpunit
OK (8 tests, 8 assertions)

TODO リスト:

  • 数を文字列にして返す
  • 3 の倍数のときは数の代わりに「Fizz」と返す
  • 5 の倍数のときは「Buzz」と返す
  • 3 と 5 両方の倍数の場合には「FizzBuzz」と返す
  • 1 から 100 までの数
  • プリントする

3.3 配列を返す機能

Red: generateList のテスト

1 から 100 までの FizzBuzz 結果を配列で返す generateList メソッドのテストを書きます。

public function test_1から100までのFizzBuzz配列を返す(): void
{
    $fizzbuzz = new FizzBuzz();
    $result = $fizzbuzz->generateList();

    $this->assertSame('1', $result[0]);
    $this->assertSame('Fizz', $result[2]);
    $this->assertSame('Buzz', $result[4]);
    $this->assertSame('FizzBuzz', $result[14]);
    $this->assertSame('100', $result[99]);
    $this->assertCount(100, $result);
}

Green: generateList の実装

/**
 * @return string[]
 */
public function generateList(): array
{
    $result = [];
    for ($i = 1; $i <= 100; $i++) {
        $result[] = $this->generate($i);
    }
    return $result;
}

PHP の $result[] は配列の末尾に要素を追加する構文です。Go の append(results, ...) に相当します。PHPDoc の @return string[] アノテーションで戻り値の型をドキュメント化しています。

$ vendor/bin/phpunit
OK (9 tests, 7 assertions)

3.4 プリント機能

Red: printFizzBuzz のテスト

public function test_FizzBuzzをプリントする(): void
{
    $fizzbuzz = new FizzBuzz();

    ob_start();
    $fizzbuzz->printFizzBuzz();
    $output = ob_get_clean();

    $lines = explode("\n", trim($output));
    $this->assertSame('1', $lines[0]);
    $this->assertSame('Fizz', $lines[2]);
    $this->assertSame('Buzz', $lines[4]);
    $this->assertSame('FizzBuzz', $lines[14]);
    $this->assertCount(100, $lines);
}

PHP の ob_start() / ob_get_clean() はアウトプットバッファリング機能で、標準出力をキャプチャできます。Go のテストで bytes.Buffer を使うパターンに相当します。

Green: printFizzBuzz の実装

public function printFizzBuzz(): void
{
    $list = $this->generateList();
    foreach ($list as $item) {
        echo $item . "\n";
    }
}
$ vendor/bin/phpunit
OK (10 tests, 12 assertions)

TODO リスト:

  • 数を文字列にして返す
  • 3 の倍数のときは数の代わりに「Fizz」と返す
  • 5 の倍数のときは「Buzz」と返す
  • 3 と 5 両方の倍数の場合には「FizzBuzz」と返す
  • 1 から 100 までの数
  • プリントする

すべての TODO が完了しました!

$ git add .
$ git commit -m 'feat: FizzBuzz の基本実装を完了'

3.5 完成したコード

src/FizzBuzz.php
<?php

namespace App;

class FizzBuzz
{
    public function generate(int $number): string
    {
        if ($number % 15 === 0) {
            return 'FizzBuzz';
        }
        if ($number % 3 === 0) {
            return 'Fizz';
        }
        if ($number % 5 === 0) {
            return 'Buzz';
        }
        return (string) $number;
    }

    /**
     * @return string[]
     */
    public function generateList(): array
    {
        $result = [];
        for ($i = 1; $i <= 100; $i++) {
            $result[] = $this->generate($i);
        }
        return $result;
    }

    public function printFizzBuzz(): void
    {
        $list = $this->generateList();
        foreach ($list as $item) {
            echo $item . "\n";
        }
    }
}

3.6 まとめ

この章では以下のことを学びました。

  • 明白な実装 でシンプルな操作を直接実装する手法
  • リファクタリング で仮実装を汎用的なロジックに変換する方法
  • PHP の % 演算子による倍数チェック
  • PHP の配列操作($result[] による要素追加)
  • PHP のアウトプットバッファリング(ob_start() / ob_get_clean())による標準出力のテスト
  • PHPDoc の @return string[] アノテーション

第 1 部では TDD の基本サイクル(Red → Green → Refactor)を体験しました。次の第 2 部では、開発環境の整備と自動化について学びます。