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

от автора

В этой серии статей мы строим программное обеспечение марсохода в соответствии со следующими спецификациями. Это позволит применить нам на практике следующие подходы:

  • 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.

Предыдущая часть: Марсоход, Посадка

ссылка на оригинал статьи https://habrahabr.ru/post/315684/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *