Марсохід, Координати посадки


У цій серії статей ми будуємо програмне забезпечення марсохода в відповідно з наступними специфікаціями. Це дозволить застосувати на практиці такі підходи:
  • Monolithic Repositories — MonoRepo (Монолітні репозиторії)
  • Command/Query Responsibility Segregation CQRS (Сегрегація відповідальності на читання і запис)
  • Event Sourcing — ES (Події як джерело)
  • Test Driven Development — TDD (Розробка через тестування)
ЗмістМарсохід, Введення
Марсохід, Ініціалізація
Марсохід, Посадка
Марсохід, Координати посадки
У попередніх частинах ми створили пакет навігації, а в ньому
LandRover
клас, який валидирует вхідні параметри для нашого першого способу використання:
Марсохід повинен буде спочатку приземлитися в заданому положенні. Положення складається з координат (
X
та
Y
, що є цілими числами) і орієнтації (значення рядка
north
,
east
,
west
або
south
).
Сьогодні ми будемо рефакторіть
LandRover
:
cd packages/navigation
git checkout 2-landing

Відповідальність
Подивившись на
LandRover
, можна знайти 2 причини зміни:
  • координати
    x
    та
    y
    може приймати
    float
    значення, або мати додаткову вісь
    z
  • орієнтація може бути в кутових градусах або мати вертикальну орієнтацію.
Це натякає на два нових класу, витягнутих з
LandRover
:
Coordinates
та
Orientation
. У цій статті ми подбаємо про координати.
Координати
Спочатку зробимо тестовий клас, використовуючи phpspec:
vendor/bin/phpspec describe 'MarsRover\Navigation\Coordinates'

З'явиться новий файл
spec/MarsRover/Navigation/CoordinatesSpec.php
:
namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Coordinates;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class CoordinatesSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(Coordinates::class);
}
}

Ми відредагуємо його, використовуючи напрацювання з тестового класу
LandRover
:
namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class CoordinatesSpec extends ObjectBehavior
{
const X = 23;
const Y = 42;

function it_has_x_coordinate()
{
$this->beConstructedWith(
self::X,
self::Y
);

$this->getX()->shouldBe(self::X);
}

function it_cannot_have_non_integer_x_coordinate()
{
$this->beConstructedWith(
'Nobody expects the Spanish Inquisition!',
self::Y
);

$this->shouldThrow(
\InvalidArgumentException::class
)->duringInstantiation();
}

function it_has_y_coordinate()
{
$this->beConstructedWith(
self::X,
self::Y
);

$this->getY()->shouldBe(self::Y);
}

function it_cannot_have_non_integer_y_coordinate()
{
$this->beConstructedWith(
self::X,
'No one expects the Spanish Inquisition!'
);

$this->shouldThrow(
\InvalidArgumentException::class
)->duringInstantiation();
}
}

Якщо запустити тести зараз, буде завантажений клас
CoordinatesSpec
:
vendor/bin/phpspec run

І він створить нам файл
src/MarsRover/Navigation/Coordinates.php
:
namespace MarsRover\Navigation;

class Coordinates
{
private $argument1;

private $argument2;

public function __construct($argument1, $argument2)
{
$this->argument1 = $argument1;
$this->argument2 = $argument2;
}

public function getX()
{
}

public function getY()
{
}
}

Тепер залишається тільки завершити те, що ми вже робили для класу
LandRover
:
namespace MarsRover\Navigation;

class Coordinates
{
private $x;
private $y;

public function __construct($x, $y)
{
if (false === is_int($x)) {
throw new \InvalidArgumentException(
'X coordinate must be an integer'
);
}
$this->x = $x;
if (false === is_int($y)) {
throw new \InvalidArgumentException(
'Y coordinate must be an integer'
);
}
$this->y = $y;
}

public function getX() : int
{
return $this->x;
}

public function getY() : int
{
return $this->y;
}
}

Запустимо тести:
vendor/bin/phpspec run

Всі зелені! Оновимо тестовий клас
LandRover
для використання в ньому нового класу координат:
namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class LandRoverSpec extends ObjectBehavior
{
const X = 23;
const Y = 42;
const ORIENTATION = 'north';

function it_has_coordinates()
{
$this->beConstructedWith(
self::X,
self::Y,
self::ORIENTATION
);

$coordinates = $this->getCoordinates();
$coordinates->getX()->shouldBe(self::X);
$coordinates->getY()->shouldBe(self::Y);
}

function it_has_an_orientation()
{
$this->beConstructedWith(
self::X,
self::Y,
self::ORIENTATION
);

$this->getOrientation()->shouldBe(self::ORIENTATION);
}

function it_cannot_have_a_non_cardinal_orientation()
{
$this->beConstructedWith(
self::X,
self::Y,
'A hareng!'
);

$this->shouldThrow(
\InvalidArgumentException::class
)->duringInstantiation();
}
}

не потрібно валідувати значення
x
та
y
, все це довіримо класу
Coordinates
, він подбає про це для нас. Тепер можна оновити клас
LandRover
:
namespace MarsRover\Navigation;

class LandRover
{
const VALID_ORIENTATIONS = ['north', 'east', 'west', 'south'];

private $coordinates;
private $orientation;

public function __construct($x, $y, $orientation)
{
$this->coordinates = new Coordinates($x, $y);
if (false === in_array($orientation, self::VALID_ORIENTATIONS, true)) {
throw new \InvalidArgumentException(
'Orientation must be one of: '
.implode(', ', self::VALID_ORIENTATIONS)
);
}
$this->orientation = $orientation;
}

public function getCoordinates() : Coordinates
{
return $this->coordinates;
}

public function getOrientation() : string
{
return $this->orientation;
}
}

Ще раз перевіримо, чи все в порядку, запустивши тести:
vendor/bin/phpspec run

Відмінно, все пройшло! Закоммитим зміни:
git add -A
git commit -m '2: Created Coordinates'

Висновок
Ми пройшли повний цикл TDD: тест, код, рефакторинг. Використання
phpspec
було дуже корисно для прототипування тестових класів, а потім і самого коду.
далі
У наступній статті ми виділимо
Orientation
LandRover
.
Попередня частина: Марсохід, Посадка
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.