Я пытаюсь осмыслить самый простой способ написания кода для чего-то вроде системы регистрации типа Yelp или FB, но
с компонентом проверки тоже. Я использую PHP.
Основным требованием может быть:
Регистрироваться:
если это типа «здание», мне нужно
если это типа «комната», мне нужно
Также есть этап «отъезда», о котором я упоминаю, чтобы вы могли понять, что это не совсем то, что Yelp, когда вы просто говорите
«Я был здесь». Это не одно действие, чтобы просто зарегистрироваться; за ним последует «проверка», о которой я здесь не буду рассказывать.
для краткости. Мой вопрос касается создания более чистых контроллеров, методов и шаблонов проектирования для структурирования кода, который
удовлетворяет вышесказанному.
Итак, запрос отправляет json, обращается к приложению (Slim), отправляется на контроллер для метода (в данном случае Post). У нас есть
их GPS-координаты, UUID здания / комнаты, в которую они проверяются, их ID (через аутентификацию) и тип
регистрации, здания или комнаты. Что-то вроде:
{
"latitude": "33.333", // these only matter if it is 'building'
"longitude": "-80.343", // these only matter if it is 'building'
"buildingUuid": "ff97f741-dba2-415a-a9e0-5a64633e13ad", // could also be 'roomUuid' ...
"type": "building", // or 'room'
"exceptionReason": null
}
У меня есть все возможности проверить их связь с координатами здания, посмотреть идентификатор здания и т. Д. Я
ищет идеи о том, как сделать этот код простым, понятным и тестируемым. С одной стороны, вы можете увидеть, как мой
операторы switch (дальний блок кода) вышли из-под контроля, но я не знаю, как еще это структурировать. Это
суть вопроса.
Без каких-либо требований: (код Feaux)
switch($type) {
case 'building':
$model = new \Namespace\Models\Building($this->container);
break;
case 'room':
$model = new \Namespace\Models\Room($this->container);
break;
default:
throw new \InvalidArgumentException("Type {$type} not understood");
}
$model->checkIn(); // polymorphic method that all 'type' classes will have, based on an interface implemented
Я пытаюсь избегать операторов switch, но в какой-то момент мне нужно что-то использовать, чтобы выяснить, какие создать экземпляр,
право? Полиморфизм здесь не помогает, так как я понимаю, что он помогает многим операторам переключения, потому что я еще не
есть объект.
Так что, мне кажется, это довольно просто. Что не ясно, так это когда я хочу добавить некоторые требования. Что такое
правильный способ обработки этого типа структуры (которая включает в себя только два типа … это может выйти из-под контроля в
торопиться).
(код Feaux)
switch ($type) {
case 'building':
$model = new \Namespace\Models\CheckInBuilding($this->container);
$alreadyInTest = new \Namespace\Models\Buildings\BuildingTestIn($this->container);
$building = new \Namespace\Models\Buildings\Building($this->getData('buildingUuid'), $this->container);
if (!$building) {
throw new Exceptions\FailBadInput('Building not found: ' . $this->getData('buildingUuid'));
}
$model->setBuilding($building); // building object used for these properties: ID, coordinates for the lat/long 'nearby' check
break;
case 'room':
$model = new \Namespace\Models\CheckInRoom($this->container);
$alreadyInTest = new \Namespace\Models\Rooms\RoomTestIn($this->container);
$room = new \Namespace\Models\Rooms\Room($this->getData('roomUuid'), $this->container);
if (!$room) {
throw new Exceptions\FailBadInput('Room not found: ' . $this->getData('roomUuid'));
}
$model->setRoom($room); // room object used for these properties: ID
break;
default:
throw new \InvalidArgumentException("Type {$type} not understood");
}
$model->setAlreadyInTest($alreadyInTest);
$model->setData($this->getData());
$model->checkIn();
//
// Now, for 'building|room', this has all the data internal to check:
// - if they are nearby (but only if in building)
// - to test if they are already logged in
// - to log the primary key ID with the request
//
//
// In addition, and not covered here, if they are not in range of the building, they can still check-in, but need to
// type a short reason why they are doing a check-in and not in nearby range ('GPS broken, etc', who knows...). So,
// would I add yet another class, instantiated here so it can be mocked in a test, and passed in empty, so it could
// eventually be used to insert an exception reason (in a different table than the check-in table)?
//
На основе шаблонов дизайна, если мне нужно добавить еще $type
Я просто делаю это здесь, что кажется довольно хорошим. Но…
это в контроллере … он злоупотребляет статусом переключателя … и кажется хрупким / хрупким из-за всех разных
вещи, которые создаются и передаются.
У меня есть DIC, но я не знаю, что это проясняет ситуацию.
Хотя это и прояснит код здесь, я не хочу создавать экземпляры каких-либо классов в реальных моделях (например, я не хочу
для создания объекта «hereInTester» внутри объекта), потому что я хочу, чтобы это было тестируемым, и делать это похоже на
сделало бы намного более сложным тестирование / макет.
Сказав это, тесты имеют ту же проблему. Существует так много катания на этих различных требованиях и как проверить
им, что тесты не очень изолированы. Я могу издеваться над объектами InInest и Building / Room, чтобы изолировать
Метод checkIn и тестирование здания / комнаты по отдельности, но чтобы протестировать их все вместе, как в интеграционном тесте, теперь я
Риск недетерминированных тестов, потому что я считаю, что это грязный подход.
Моей последней мыслью было бы что-то вроде этого, но я слишком переживаю за то, что не откормлю контроллер: (код feaux)
switch($type) {
case 'building':
$alreadyInTest = new \Namespace\Models\Buildings\BuildingTestIn($this->container);
if($alreadyInTest->isIn()) {
throw new \InvalidArgumentException('You are already checked in to a building');
}
$building = new \Namespace\Models\Buildings\Building($this->getData('buildingUuid'), $this->container);
if (!$building) {
throw new Exceptions\FailBadInput('Building not found: ' . $this->getData('buildingUuid'));
}
$model = new \Namespace\Models\Building($this->container);
break;
case 'room':
$alreadyInTest = new \Namespace\Models\Rooms\RoomTestIn($this->container);
if($alreadyInTest->isIn()) {
throw new \InvalidArgumentException('You are already checked in to a room');
}
$room = new \Namespace\Models\Rooms\Room($this->getData('roomUuid'), $this->container);
if (!$room) {
throw new Exceptions\FailBadInput('Room not found: ' . $this->getData('roomUuid'));
}
$model = new \Namespace\Models\Room($this->container);
break;
default:
throw new \InvalidArgumentException("Type {$type} not understood");
}
$model->setData($this->getData());
$model->checkIn();
Опять же, я чувствую, что должен абстрагировать эти два if / throws (для каждого случая) в другом месте, но это, похоже, не делает этого
более простой (также: я признаю, что это не сложный пример … пока), и контроллер не является более тонким
в любом примере. Я чувствую, что последний пример для меня более понятен. Суть для меня в том, что каждый раз что-то будет
добавлено, это будет означать добавление еще к инструкции switch. Я думаю, что полиморфная система будет лучше, но так как
для проверки требований потребуются внешние классы, которые я должен был бы создать и передать тонну
объекты в любом случае, что делает его таким же сложным. Создание экземпляров классов внутри каждого объекта регистрации не так тестируемо. я
Я подумал, что, возможно, шаблоны Chain of Responsibility или Command могут быть полезны, но, похоже, не совсем подходят.
Я хожу по кругу.
Итак, один из них — лучший способ, или я мог бы сделать что-то лучше?
Задача ещё не решена.
Других решений пока нет …