La plupart du temps, lorsque j'écris du code qui gère la réponse pour un certain appel de fonction, j'obtiens la structure de code suivante:
exemple: il s'agit d'une fonction qui gérera l'authentification pour un système de connexion
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.
}
}
Problème
- Comme vous pouvez le voir, le code est construit sur une
if/else
structure, ce qui signifie qu'un nouveau statut d'échec signifie que je dois ajouter uneelse if
instruction qui constitue une violation du principe ouvert et fermé . - J'ai l'impression que la fonction a différentes couches d'abstraction car je peux simplement augmenter le compteur d'essais de connexion dans un gestionnaire, mais faire des choses plus sérieuses dans un autre.
- Certaines fonctions sont répétées
increase the login trials
par exemple.
J'ai pensé à convertir le multiple if/else
en un motif d'usine, mais je n'ai utilisé l'usine que pour créer des objets et non pour modifier les comportements. Quelqu'un at-il une meilleure solution pour cela?
Remarque:
Ceci est juste un exemple d'utilisation d'un système de connexion. Je demande une solution générale à ce comportement en utilisant un modèle OO bien construit. Ce type de if/else
gestionnaires apparaît à trop d'endroits dans mon code et je viens d'utiliser le système de connexion comme exemple simple et facile à expliquer. Mes cas d'utilisation réels sont trop compliqués à publier ici. :RÉ
Veuillez ne pas limiter votre réponse au code PHP et n'hésitez pas à utiliser la langue que vous préférez.
MISE À JOUR
Un autre exemple de code plus compliqué juste pour clarifier ma question:
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();
}
}
but de la fonction:
- Je vends des jeux sur ebay.
- Si un client souhaite annuler sa commande et récupérer son argent (c'est-à-dire un remboursement), je dois d'abord ouvrir un "Litige" sur ebay.
- Une fois qu'un litige est ouvert, je dois attendre que le client confirme qu'il accepte le remboursement (idiot car c'est lui qui m'a dit de rembourser, mais c'est comme ça que ça fonctionne sur ebay).
- Cette fonction obtient tous les litiges ouverts par moi et vérifie périodiquement leur statut pour voir si le client a répondu au litige ou non.
- Le client peut accepter (puis je rembourse) ou refuser (puis je recule) ou peut ne pas répondre pendant 7 jours (je clore le litige moi-même puis rembourser).
getOrderStrategy
s'agit d'une méthode d'usine qui renvoie unstrategy
objet en fonction du statut de la commande, mais quelles sont les fonctionspreProcess()
etpreProcess()
. Et pourquoi es-tu passé$this
àupdateOrderHistory($this)
?Le modèle de stratégie est une bonne suggestion si vous voulez vraiment décentraliser votre logique, mais cela semble être un excès d'indirection pour des exemples aussi petits que le vôtre. Personnellement, j'emploierais le modèle "écrire des fonctions plus petites", comme:
la source
Lorsque vous commencez à avoir un tas d'instructions if / then / else pour gérer un état, considérez le modèle d'état .
Il y avait une question sur une manière particulière de l'utiliser: cette mise en œuvre du modèle d'état a-t-elle un sens?
Je suis nouveau dans ce bagout, mais j'ai quand même mis en place la réponse pour m'assurer de bien comprendre quand l'utiliser (éviter les «tous les problèmes ressemblent à des clous pour un marteau».).
la source
Comme je l'ai dit dans mes commentaires, la logique complexe ne change vraiment rien.
Vous souhaitez traiter une commande contestée. Il y a plusieurs façons de procéder. Le type de commande contesté peut être
Enum
:Il y a plusieurs façons de procéder. Vous pouvez avoir hiérarchie d'héritage de
Order
,DisputedOrder
,DisputedOrderLessThan7Days
,DisputedOrderCanceled
, etc. Ce n'est pas beau, mais il fonctionne également.Dans mon exemple ci-dessus, je regarde le type de commande et j'obtiens une stratégie pertinente pour cela. Vous pouvez encapsuler ce processus dans une usine:
Cela examinerait le type de commande et vous donnerait une stratégie correcte pour ce type de commande.
Vous pourriez vous retrouver avec quelque chose comme:
Réponse originale, plus pertinente car je pensais que vous vouliez quelque chose de plus simple:
Je vois ici les préoccupations suivantes:
Je ferais ce qui suit:
Actuellement, votre exemple a trop de responsabilités. Tout ce que j'ai fait, c'était d'encapsuler ces responsabilités dans les méthodes. Le code semble plus propre et vous n'avez pas de déclarations de condition partout.
Factory encapsule la construction d'objets. Vous n'avez pas besoin d'encapsuler la construction de quoi que ce soit dans votre exemple, tout ce que vous avez à faire est de séparer vos préoccupations.
la source