Die meiste Zeit, wenn ich Code schreibe, der die Antwort für einen bestimmten Funktionsaufruf verarbeitet, erhalte ich die folgende Codestruktur:
Beispiel: Dies ist eine Funktion, die die Authentifizierung für ein Anmeldesystem übernimmt
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.
}
}
Problem
- Wie Sie sehen können, basiert der Code auf einer
if/else
Struktur, was bedeutet, dass ein neuer Fehlerstatus bedeutet, dass ich eineelse if
Anweisung hinzufügen muss , die einen Verstoß gegen das Open Closed-Prinzip darstellt . - Ich habe das Gefühl, dass die Funktion unterschiedliche Abstraktionsebenen aufweist, da ich möglicherweise nur den Zähler für Anmeldeversuche in einem Handler erhöhen, in einem anderen jedoch ernstere Aufgaben ausführen kann.
- Einige der Funktionen werden beispielsweise wiederholt
increase the login trials
.
Ich habe darüber nachgedacht, das Multiple if/else
in ein Factory-Muster zu konvertieren , aber ich habe Factory nur verwendet, um Objekte zu erstellen, ohne das Verhalten zu ändern. Hat jemand eine bessere Lösung dafür?
Hinweis:
Dies ist nur ein Beispiel für die Verwendung eines Anmeldesystems. Ich bitte um eine allgemeine Lösung für dieses Verhalten unter Verwendung eines gut aufgebauten OO-Musters. Diese Art von if/else
Handlern erscheint an zu vielen Stellen in meinem Code und ich habe das Anmeldesystem nur als einfaches, leicht zu erklärendes Beispiel verwendet. Meine wirklichen Anwendungsfälle sind zu kompliziert, um sie hier zu posten. : D.
Bitte beschränken Sie Ihre Antwort nicht auf PHP-Code und verwenden Sie die von Ihnen bevorzugte Sprache.
AKTUALISIEREN
Ein weiteres komplizierteres Codebeispiel, um meine Frage zu klären:
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();
}
}
Funktionszweck:
- Ich verkaufe Spiele bei ebay.
- Wenn ein Kunde seine Bestellung stornieren möchte und sein Geld zurückerhält (dh eine Rückerstattung), muss ich zuerst einen "Streit" bei ebay eröffnen.
- Sobald ein Streitfall eröffnet ist, muss ich warten, bis der Kunde bestätigt, dass er der Rückerstattung zustimmt (albern, da er mir gesagt hat, ich solle zurückerstatten, aber so funktioniert es bei ebay).
- Diese Funktion öffnet alle Streitigkeiten von mir und überprüft regelmäßig ihren Status, um festzustellen, ob der Kunde auf den Streit geantwortet hat oder nicht.
- Der Kunde kann zustimmen (dann erstatte ich) oder ablehnen (dann rolle ich zurück) oder 7 Tage lang nicht antworten (ich schließe den Streit selbst und erstatte dann).
getOrderStrategy
es sich um eine Factory-Methode handelt, die einstrategy
Objekt abhängig vom Auftragsstatus zurückgibt , aber was sind diepreProcess()
undpreProcess()
Funktionen. Auch warum übergeben Sie$this
anupdateOrderHistory($this)
?Das Strategiemuster ist ein guter Vorschlag, wenn Sie Ihre Logik wirklich dezentralisieren möchten, aber es scheint ein Indirektions-Overkill für Beispiele zu sein, die so klein sind wie Ihre. Persönlich würde ich das Muster "kleinere Funktionen schreiben" verwenden, wie:
quelle
Berücksichtigen Sie das Statusmuster, wenn Sie eine Reihe von if / then / else-Anweisungen zur Behandlung eines Status haben .
Es gab eine Frage zu einer bestimmten Art der Verwendung: Ist diese Implementierung des Zustandsmusters sinnvoll?
Ich bin neu in diesem Muster, aber ich habe die Antwort trotzdem angegeben, um sicherzustellen, dass ich verstehe, wann ich sie verwenden soll (Vermeiden Sie "Alle Probleme sehen für einen Hammer wie Nägel aus").
quelle
Wie ich in meinen Kommentaren sagte, ändert komplexe Logik nichts wirklich.
Sie möchten eine umstrittene Bestellung bearbeiten. Dafür gibt es mehrere Möglichkeiten. Umstrittene Auftragsart kann sein
Enum
:Es gibt viele Möglichkeiten, dies zu tun. Sie können Vererbungshierarchie von haben
Order
,DisputedOrder
,DisputedOrderLessThan7Days
,DisputedOrderCanceled
etc. Das ist nicht schön, aber es würde auch funktionieren.In meinem obigen Beispiel schaue ich mir die Auftragsart an und erhalte eine relevante Strategie dafür. Sie können diesen Prozess in eine Fabrik einkapseln:
Dies würde sich die Auftragsart ansehen und Ihnen eine korrekte Strategie für diese Auftragsart geben.
Sie könnten am Ende etwas in der Art von:
Ursprüngliche Antwort, nicht mehr relevant, da ich dachte, Sie wollten etwas Einfacheres:
Ich sehe hier folgende Bedenken:
Ich würde folgendes tun:
Derzeit hat Ihr Beispiel zu viele Verantwortlichkeiten. Alles, was ich getan habe, war, diese Verantwortlichkeiten in Methoden zusammenzufassen. Code sieht sauberer aus und Sie haben nicht überall Bedingungsanweisungen.
Factory kapselt die Konstruktion von Objekten. Sie müssen die Konstruktion von nichts in Ihrem Beispiel zusammenfassen. Alles, was Sie tun müssen, ist, Ihre Bedenken zu trennen.
quelle