Na maioria das vezes, quando estou escrevendo algum código que lida com a resposta para uma determinada chamada de função, recebo a seguinte estrutura de código:
exemplo: Esta é uma função que manipulará a autenticação para um sistema de login
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.
}
}
Problema
- Como você pode ver, o código é construído em uma
if/else
estrutura, o que significa que um novo status de falha significa que preciso adicionar umaelse if
declaração que seja uma violação do Princípio Aberto Fechado . - Sinto que a função tem diferentes camadas de abstração, pois posso apenas aumentar o contador de tentativas de login em um manipulador, mas fazer coisas mais sérias em outro.
- Algumas das funções são repetidas,
increase the login trials
por exemplo.
Pensei em converter o múltiplo if/else
em um padrão de fábrica, mas só usei o factory para criar objetos e não alterar comportamentos. Alguém tem uma solução melhor para isso?
Nota:
Este é apenas um exemplo usando um sistema de login. Estou pedindo uma solução geral para esse comportamento usando um padrão OO bem construído. Esse tipo de if/else
manipulador aparece em muitos lugares no meu código e eu apenas usei o sistema de login como um exemplo simples e fácil de explicar. Meus casos de uso reais são muito complicados para postar aqui. : D
Por favor, não limite sua resposta ao código PHP e sinta-se à vontade para usar a linguagem de sua preferência.
ATUALIZAR
Outro exemplo de código mais complicado apenas para esclarecer minha pergunta:
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();
}
}
finalidade da função:
- Eu estou vendendo jogos no ebay.
- Se um cliente deseja cancelar seu pedido e receber seu dinheiro de volta (ou seja, um reembolso), primeiro devo abrir uma "disputa" no ebay.
- Depois que uma disputa é aberta, devo esperar que o cliente confirme que ele concorda com o reembolso (por mais tolo que ele tenha me dito para reembolsar, mas é assim que funciona no ebay).
- Essa função obtém todas as disputas abertas por mim e verifica seus status periodicamente para ver se o cliente respondeu ou não à disputa.
- O cliente pode concordar (depois reembolso) ou recusar (depois reverter) ou pode não responder por 7 dias (eu mesmo encerro a disputa e reembolso).
getOrderStrategy
é um método de fábrica que retorna umstrategy
objeto dependendo do status do pedido, mas quais são as funçõespreProcess()
epreProcess()
. Além disso, por que você passar$this
paraupdateOrderHistory($this)
?O padrão de estratégia é uma boa sugestão se você realmente deseja descentralizar sua lógica, mas parece um exagero indireto para exemplos tão pequenos quanto o seu. Pessoalmente, eu empregaria o padrão "escrever funções menores", como:
fonte
Quando você começar a ter várias instruções if / then / else para manipular um status, considere o Padrão de Estado .
Havia uma pergunta sobre uma maneira específica de usá-lo: essa implementação do padrão de estado faz sentido?
Eu sou novo nesse padrão, mas, de qualquer maneira, respondo a resposta para ter certeza de que entenderá quando usá-lo (evite "todos os problemas parecem unhas de um martelo").
fonte
Como eu disse nos meus comentários, a lógica complexa realmente não muda nada.
Você deseja processar um pedido contestado. Existem várias maneiras de fazer isso. O tipo de pedido contestado pode ser
Enum
:Existem muitas maneiras de fazer isso. Você pode ter hierarquia de herança
Order
,DisputedOrder
,DisputedOrderLessThan7Days
,DisputedOrderCanceled
, etc. Isto não é bom, mas também funcionaria.No meu exemplo acima, analiso o tipo de pedido e obtenho uma estratégia relevante para isso. Você pode encapsular esse processo em uma fábrica:
Isso analisaria o tipo de pedido e forneceria uma estratégia correta para esse tipo de pedido.
Você pode acabar com algo nas linhas de:
Resposta original, não é mais relevante como eu pensava que você estava procurando por algo mais simples:
Vejo as seguintes preocupações aqui:
Eu faria o seguinte:
Atualmente, seu exemplo tem muitas responsabilidades. Tudo o que fiz foi encapsular essas responsabilidades dentro dos métodos. O código parece mais limpo e você não tem instruções de condição em todo o lugar.
A fábrica encapsula a construção de objetos. Você não precisa encapsular a construção de nada no seu exemplo, tudo o que você precisa fazer é separar suas preocupações.
fonte