#php #oop #symfony #design-patterns
#php #ооп #symfony #шаблоны проектирования
Вопрос:
Итак, я создаю временную шкалу, где для текущего месяца я вычисляю некоторые ключевые показатели эффективности, чтобы показать рядом с временной шкалой. Как итоги и улучшение по сравнению с прошлым месяцем.
Когда я выбираю «Все время», я хотел бы показать все те же ключевые показатели эффективности, кроме одного. Улучшение по сравнению с прошлым месяцем будет средним за все время для каждого пользователя.
Какой шаблон проектирования я использую для реализации этой функциональности? Поскольку оба вызова API для моего серверной части Symfony2, вероятно, будут использовать одну и ту же конечную точку, только с другим параметром МЕСЯЦ против всего времени (или, возможно, с другой конечной точкой). И они на 90% будут выполнять те же вычисления, я бы хотел сделать это как можно более общим, чтобы разрешить, возможно, другие параметры в будущем.
Должен ли я создать стратегию, подобную MonthFilterStrategy и AllTimeFilterStrategy, и установить контекст на основе параметра или есть лучший шаблон проектирования, который применим к этому варианту использования.
Ответ №1:
- Создайте отдельные методы / конечные точки в своих контроллерах и на бизнес-уровне
- Если эти методы используют какой-то код, переместите его в еще один непубличный метод
URL-адреса:
http://my-domain.com/get-results/2014/06
http://my-domain.com/get-results/all-time
Контроллеры / действия:
public function getResultsByMonthAction($year, $month) {
$from = DateTime::createFromFormat('Y-m-d', sprintf('%d-%d-01', $year, $month));
$from->setTime(0, 0, 0);
$to = $from->modify('next month');
$results = $this->myService->getResultsByDateRange($from, $to);
return $this->handleCommonCode($results);
}
public function getAllTimeResultsAction() {
$results = $this->myService->getAllTimeResults();
return $this->handleCommonCode($results);
}
private function handleCommonCode($results) {
...
$abc = $this->processResultsSomehow($results);
return $this->renderTemplate('...', [
'someExtraData' => $abc,
'results' => $results
]);
}
Обслуживание:
public function getResultsByDateRange(DateTime $from, DateTime $to) {
if ($from > $to) {
throw new LogicException('Invalid range. $from must be lower than $to.');
}
$criteria = [
'date-rage' => [$from, $to]
];
return $this->getResults($criteria);
}
public function getAllTimeResults() {
return $this->getResults();
}
protected function getResults(array $criteria = []) {
// let's assume you're quering database (using Doctrine)
$queryBuilder = ...;
if (isset($criteria['date-rage'])) {
$queryBuilder
->andWhere('abc.someDate BETWEEN :dateRangeStart AND :dateRangeEnd')
->addParameters([
'dateRangeStart' => $criteria['date-rage'][0],
'dateRangeEnd' => $criteria['date-rage'][1]
]);
}
return $queryBuilder->getQuery()->getResult();
}
Этот подход требует немного больше кода, но он имеет свои преимущества:
- Гораздо проще документировать и понимать API,
- Нет необходимости в бесчисленных
if
заявлениях, - Проще расширять / переопределять, поскольку каждая часть кода находится в отдельном методе,
- Упрощение отладки и обслуживания,
- Каждый метод может содержать некоторую индивидуальную логику,
- Поддержка сторонних инструментов (IDE, анализаторы кода и т.д.)
Ответ №2:
Я бы предположил, что если нет параметров, с помощью которых можно охватить дату, вы бы применили свои вычисления через N месяцев, где N — некоторый разумный предел (или нет; YMMV). Учитывая это, обработка должна выполняться для данных за N месяцев и никогда не предполагать, что для обработки может быть только один объект. Всегда предполагайте, что вы будете обрабатывать данные за N месяцев, а затем позволять наличию параметров области видимости / фильтрации управлять пользовательским интерфейсом.