Przez większość czasu, gdy piszę kod, który obsługuje odpowiedź na określone wywołanie funkcji, otrzymuję następującą strukturę kodu:
przykład: jest to funkcja, która będzie obsługiwać uwierzytelnianie w systemie logowania
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
- Jak widać, kod jest zbudowany na
if/else
strukturze, co oznacza, że nowy stan awarii oznacza, że muszę dodaćelse if
oświadczenie, które narusza zasadę otwartej zamkniętej zasady . - Mam wrażenie, że funkcja ma różne warstwy abstrakcji, ponieważ mogę po prostu zwiększyć licznik prób logowania w jednym module obsługi, ale zrobić coś poważniejszego w innym.
- Niektóre funkcje są
increase the login trials
na przykład powtarzane .
Myślałem o przekształceniu wielokrotności if/else
na wzorzec fabryczny, ale użyłem fabryki tylko do tworzenia obiektów, a nie zmiany zachowań. Czy ktoś ma na to lepsze rozwiązanie?
Uwaga:
To tylko przykład z wykorzystaniem systemu logowania. Proszę o ogólne rozwiązanie tego zachowania przy użyciu dobrze zbudowanego wzorca OO. Tego rodzaju if/else
programy obsługi pojawiają się w zbyt wielu miejscach w moim kodzie i właśnie użyłem systemu logowania jako prostego, łatwego do wyjaśnienia przykładu. Moje prawdziwe przypadki użycia są zbyt skomplikowane, aby opublikować tutaj. :RE
Nie ograniczaj swojej odpowiedzi do kodu PHP i skorzystaj z preferowanego języka.
AKTUALIZACJA
Kolejny bardziej skomplikowany przykład kodu, aby wyjaśnić moje pytanie:
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();
}
}
cel funkcji:
- Sprzedaję gry w serwisie eBay.
- Jeśli klienci chcą anulować swoje zamówienie i odzyskać pieniądze (tj. Zwrot pieniędzy), najpierw muszę otworzyć „spór” w serwisie eBay.
- Gdy spór zostanie otwarty, muszę poczekać, aż klient potwierdzi, że zgadza się na zwrot pieniędzy (głupie, ponieważ to on kazał mi zwrócić pieniądze, ale tak to działa w serwisie eBay).
- Ta funkcja otwiera wszystkie spory przeze mnie i okresowo sprawdza ich status, aby sprawdzić, czy klient odpowiedział na spór, czy nie.
- Klient może wyrazić zgodę (następnie zwracam pieniądze) lub odmówić (następnie wycofuję) lub może nie odpowiadać przez 7 dni (sam zamykam spór, a następnie zwracam pieniądze).
getOrderStrategy
jest to metoda fabryczna, która zwracastrategy
obiekt w zależności od statusu zamówienia, ale jakie są funkcjepreProcess()
ipreProcess()
. Również dlaczego przejść$this
doupdateOrderHistory($this)
?Wzorzec strategii jest dobrą sugestią, jeśli naprawdę chcesz zdecentralizować swoją logikę, ale wydaje się, że przesada pośrednia dla przykładów tak małych jak twoje. Osobiście zastosowałbym wzorzec „pisz mniejsze funkcje”, na przykład:
źródło
Kiedy zaczniesz mieć kilka instrukcji if / then / else do obsługi statusu, rozważ wzorzec stanu .
Pojawiło się pytanie o konkretny sposób jego wykorzystania: Czy ta implementacja wzorca stanu ma sens?
Jestem nowy w tym tupocie, ale i tak postawiłem odpowiedź, aby upewnić się, że rozumiem, kiedy jej użyć (Unikaj „wszystkie problemy wyglądają jak gwoździe do młota”).
źródło
Jak powiedziałem w moich komentarzach, złożona logika tak naprawdę niczego nie zmienia.
Chcesz zrealizować sporne zamówienie. Można to zrobić na wiele sposobów. Spornym rodzajem zamówienia może być
Enum
:Można to zrobić na wiele sposobów. Można mieć hierarchię dziedziczenia
Order
,DisputedOrder
,DisputedOrderLessThan7Days
,DisputedOrderCanceled
, itd. Nie jest to miłe, ale również praca.W powyższym przykładzie patrzę na rodzaj zamówienia i szukam odpowiedniej strategii. Możesz zamknąć ten proces w fabryce:
Spowoduje to sprawdzenie rodzaju zamówienia i zapewni właściwą strategię dla tego rodzaju zamówienia.
Możesz skończyć z czymś w rodzaju:
Oryginalna odpowiedź, nieistotna, ponieważ myślałem, że szukasz czegoś prostszego:
Widzę tutaj następujące obawy:
Zrobiłbym następujące:
Obecnie twój przykład ma zbyt wiele obowiązków. Wszystko, co zrobiłem, to ujęcie tych obowiązków w ramach metod. Kod wygląda na bardziej przejrzysty i nie ma wszędzie instrukcji warunkowych.
Fabryka obudowuje konstrukcję obiektów. Nie musisz ujmować konstrukcji niczego w swoim przykładzie, wszystko, co musisz zrobić, to oddzielić swoje obawy.
źródło