昨天成功處理了 1、2、3,但 4 輸出 "IIII" 而非 "IV"。今天用 TDD 處理羅馬數字的減法規則。
核心規則:
建立 tests/Unit/Day12/RomanNumeralTest.php
<?php
use App\Roman\RomanNumeral;
test('converts 1 to I', fn() => expect((new RomanNumeral())->convert(1))->toBe('I'));
test('converts 2 to II', fn() => expect((new RomanNumeral())->convert(2))->toBe('II'));
test('converts 3 to III', fn() => expect((new RomanNumeral())->convert(3))->toBe('III'));
test('converts 4 to IV', fn() => expect((new RomanNumeral())->convert(4))->toBe('IV'));
測試失敗 🔴:Expected: "IV", Received: "IIII"
更新 app/Roman/RomanNumeral.php
:
public function convert(int $number): string
{
if ($number === 4) return 'IV';
$result = '';
for ($i = 0; $i < $number; $i++) {
$result .= 'I';
}
return $result;
}
測試通過! 🟢
加入測試:
test('converts 5 to V', fn() => expect((new RomanNumeral())->convert(5))->toBe('V'));
修改實作:
public function convert(int $number): string
{
if ($number === 5) return 'V';
if ($number === 4) return 'IV';
$result = '';
for ($i = 0; $i < $number; $i++) {
$result .= 'I';
}
return $result;
}
加入測試:
test('converts 6 to VI', fn() => expect((new RomanNumeral())->convert(6))->toBe('VI'));
test('converts 7 to VII', fn() => expect((new RomanNumeral())->convert(7))->toBe('VII'));
test('converts 8 to VIII', fn() => expect((new RomanNumeral())->convert(8))->toBe('VIII'));
重構 實作:
public function convert(int $number): string
{
if ($number >= 5) {
return 'V' . $this->convertOnes($number - 5);
}
if ($number === 4) return 'IV';
return $this->convertOnes($number);
}
private function convertOnes(int $number): string
{
$result = '';
for ($i = 0; $i < $number; $i++) {
$result .= 'I';
}
return $result;
}
加入測試:
test('converts 9 to IX', fn() => expect((new RomanNumeral())->convert(9))->toBe('IX'));
測試失敗 🔴:輸出 "VIIII" 而非 "IX"
更新 實作:
public function convert(int $number): string
{
if ($number === 9) return 'IX';
if ($number >= 5) {
return 'V' . $this->convertOnes($number - 5);
}
if ($number === 4) return 'IV';
return $this->convertOnes($number);
}
加入測試:
test('converts 10 to X', fn() => expect((new RomanNumeral())->convert(10))->toBe('X'));
更新 實作:
public function convert(int $number): string
{
if ($number === 10) return 'X';
if ($number === 9) return 'IX';
if ($number >= 5) {
return 'V' . $this->convertOnes($number - 5);
}
if ($number === 4) return 'IV';
return $this->convertOnes($number);
}
測試通過! 🟢
重構 成映射表形式:
<?php
namespace App\Roman;
class RomanNumeral
{
private array $romanNumerals = [
10 => 'X', 9 => 'IX', 5 => 'V', 4 => 'IV', 1 => 'I'
];
public function convert(int $number): string
{
$result = '';
foreach ($this->romanNumerals as $value => $numeral) {
while ($number >= $value) {
$result .= $numeral;
$number -= $value;
}
}
return $result;
}
}
測試通過! ✅
透過 TDD,我們發現羅馬數字的重要模式:
完整測試 tests/Unit/Day12/RomanNumeralTest.php
<?php
use App\Roman\RomanNumeral;
// 測試 1-5
test('converts 1 to I', fn() => expect((new RomanNumeral())->convert(1))->toBe('I'));
test('converts 2 to II', fn() => expect((new RomanNumeral())->convert(2))->toBe('II'));
test('converts 3 to III', fn() => expect((new RomanNumeral())->convert(3))->toBe('III'));
test('converts 4 to IV', fn() => expect((new RomanNumeral())->convert(4))->toBe('IV'));
test('converts 5 to V', fn() => expect((new RomanNumeral())->convert(5))->toBe('V'));
// 測試 6-10
test('converts 6 to VI', fn() => expect((new RomanNumeral())->convert(6))->toBe('VI'));
test('converts 7 to VII', fn() => expect((new RomanNumeral())->convert(7))->toBe('VII'));
test('converts 8 to VIII', fn() => expect((new RomanNumeral())->convert(8))->toBe('VIII'));
test('converts 9 to IX', fn() => expect((new RomanNumeral())->convert(9))->toBe('IX'));
test('converts 10 to X', fn() => expect((new RomanNumeral())->convert(10))->toBe('X'));
完整實作 app/Roman/RomanNumeral.php
<?php
namespace App\Roman;
class RomanNumeral
{
private array $romanNumerals = [
10 => 'X', 9 => 'IX', 5 => 'V', 4 => 'IV', 1 => 'I'
];
public function convert(int $number): string
{
$result = '';
foreach ($this->romanNumerals as $value => $numeral) {
while ($number >= $value) {
$result .= $numeral;
$number -= $value;
}
}
return $result;
}
}
執行測試:
./vendor/bin/pest tests/Unit/Day12/RomanNumeralTest.php
所有測試通過!我們有了處理 1-10 的羅馬數字轉換器。
試著思考以下問題:
今天我們成功實作了 1-10 的羅馬數字轉換,從最簡單的 if-else 開始,逐步演進到優雅的映射表模式。這就是 TDD 的魅力 - 讓我們的程式碼隨著測試逐步進化。
每個測試都像是一個小小的里程碑,紅燈告訴我們方向,綠燈確認我們走對了路,重構讓我們的程式碼更加優雅。這就是 TDD 的節奏感!
今天的 TDD 之旅就到這裡,明天我們繼續深入探索! 🚀