Большую часть времени, когда я пишу некоторый код, который обрабатывает ответ для определенного вызова функции, я получаю следующую структуру кода:
пример: это функция, которая будет обрабатывать аутентификацию для системы входа
class Authentication{
function login(){ //This function is called from my Controller
$result=$this->authenticate($username,$password);
if($result=='wrong password'){
//increase the login trials counter
//send mail to admin
//store visitor ip
}else if($result=='wrong username'){
//increase the login trials counter
//do other stuff
}else if($result=='login trials exceeded')
//do some stuff
}else if($result=='banned ip'){
//do some stuff
}else if...
function authenticate($username,$password){
//authenticate the user locally or remotely and return an error code in case a login in fails.
}
}
проблема
- Как вы можете видеть, код построен на
if/else
структуре, которая означает, что новый статус ошибки будет означать, что мне нужно добавитьelse if
утверждение, которое является нарушением принципа открытого закрытого доступа . - У меня возникает ощущение, что функция имеет разные уровни абстракции, поскольку я могу просто увеличить счетчик попыток входа в систему в одном обработчике, но делать более серьезные вещи в другом.
- Некоторые функции повторяются,
increase the login trials
например.
Я думал о преобразовании кратного if/else
в шаблон фабрики, но я использовал фабрику только для создания объектов, не меняющих поведения. У кого-нибудь есть лучшее решение для этого?
Замечания:
Это всего лишь пример использования системы входа в систему. Я прошу общее решение этого поведения, используя хорошо построенный шаблон OO. Этот тип if/else
обработчиков встречается в слишком многих местах в моем коде, и я просто использовал систему входа в систему как простой и понятный пример. Мои реальные варианты использования слишком сложны для публикации здесь. : D
Пожалуйста, не ограничивайте свой ответ PHP-кодом и не стесняйтесь использовать язык, который вы предпочитаете.
ОБНОВИТЬ
Еще один более сложный пример кода, чтобы прояснить мой вопрос:
public function refundAcceptedDisputes() {
$this->getRequestedEbayOrdersFromDB(); //get all disputes requested on ebay
foreach ($this->orders as $order) { /* $order is a Doctrine Entity */
try {
if ($this->isDisputeAccepted($order)) { //returns true if dispute was accepted
$order->setStatus('accepted');
$order->refund(); //refunds the order on ebay and internally in my system
$this->insertRecordInOrderHistoryTable($order,'refunded');
} else if ($this->isDisputeCancelled($order)) { //returns true if dispute was cancelled
$order->setStatus('cancelled');
$this->insertRecordInOrderHistory($order,'cancelled');
$order->rollBackRefund(); //cancels the refund on ebay and internally in my system
} else if ($this->isDisputeOlderThan7Days($order)) { //returns true if 7 days elapsed since the dispute was opened
$order->closeDispute(); //closes the dispute on ebay
$this->insertRecordInOrderHistoryTable($order,'refunded');
$order->refund(); //refunds the order on ebay and internally in my system
}
} catch (Exception $e) {
$order->setStatus('failed');
$order->setErrorMessage($e->getMessage());
$this->addLog();//log error
}
$order->setUpdatedAt(time());
$order->save();
}
}
Назначение функции:
- Я продаю игры на Ebay.
- Если клиент желает отменить свой заказ и получить свои деньги обратно (т.е. возмещение), я должен сначала открыть «Спор» на Ebay.
- После того, как спор открыт, я должен ждать, пока клиент подтвердит, что он согласен на возмещение (глупо, потому что он тот, кто сказал мне возместить, но это работает на ebay).
- Эта функция получает все открытые мной споры и периодически проверяет их статусы, чтобы узнать, ответил клиент на этот спор или нет.
- Клиент может согласиться (затем я верну деньги) или отказать (тогда я сделаю откат), или может не отвечать в течение 7 дней (я сам закрываю спор, а затем возвращаю деньги).
getOrderStrategy
это фабричный метод , который возвращаетstrategy
объект в зависимости от статуса заказа, но каковыpreProcess()
иpreProcess()
функции. И почему ты перешел$this
наupdateOrderHistory($this)
?Шаблон стратегии является хорошим предложением, если вы действительно хотите децентрализовать свою логику, но это кажется избыточным косвенным влиянием для таких маленьких примеров, как ваш. Лично я бы использовал шаблон «написать меньшие функции», например:
источник
Когда вы начнете иметь кучу операторов if / then / else для обработки состояния, рассмотрите шаблон состояния .
Возник вопрос о конкретном способе его использования: имеет ли смысл такая реализация шаблона состояния?
Я новичок в этой скороговорке, но я все равно поставил ответ, чтобы убедиться, что понимаю, когда его использовать (избегайте «все проблемы выглядят как гвозди к молотку»).
источник
Как я уже говорил в моих комментариях, сложная логика ничего не меняет.
Вы хотите обработать спорный заказ. Есть несколько способов сделать это. Спорный тип заказа может быть
Enum
:Есть много способов сделать это. Вы можете иметь иерархию наследования
Order
,DisputedOrder
,DisputedOrderLessThan7Days
,DisputedOrderCanceled
и т.д. Это не хорошо, но это также будет работать.В моем примере выше я смотрю на тип заказа и получаю соответствующую стратегию для этого. Вы можете заключить этот процесс в фабрику:
Это будет смотреть на тип заказа и даст вам правильную стратегию для этого типа заказа.
Вы можете получить что-то вроде:
Оригинальный ответ, более не актуален, так как я думал, что вы ищете что-то попроще:
Я вижу следующие проблемы здесь:
Я бы сделал следующее:
В настоящее время у вашего примера слишком много обязанностей. Все, что я сделал, это заключил эти обязанности в методы. Код выглядит чище, и у вас нет условных выражений повсюду.
Завод инкапсулирует строительство объектов. Вам не нужно инкапсулировать конструкцию чего-либо в вашем примере, все, что вам нужно сделать, это отделить ваши проблемы.
источник