Refaktoryzacja Strategiami
Kolejny sposób na refaktoryzację kodu. Bardzo zmyślny i elegancki, polimorficzny . Jeden cel - wiele sposobów/strategii/algorytmów/dróg...
Na czym on polega?
Metoda ta polega na rozdzieleniu różnych algorytmów, których zadaniem jest osiągnięcie ogólnie jednego i tego samego celu (pewien wspólny cel). Zamiast sprawdzać okoliczności stosując warunki (instrukcje warunkowe if) a następnie wykonując pewne działania - to zamykamy te działania w osobnych klasach (klasy strategii, strategie) a okoliczności raz sprawdzane (przez metodę lub klasę za to odpowiedzialną) zadecydują o adekwatnej strategii.
Zazwyczaj zamiast tej metody stosuje się proste i szybkie sprawdzenia (ify). Sprawdzamy przeróżne okoliczności od których zależy co nastąpi dalej. Jednak łamie to wiele zasad i dobrych praktyk, nie jest skalowalne a jest wręcz nieeleganckie i niechlujne. Prowadzi do zepsucia i trudnościach w utrzymaniu kodu.
Kiedy skorzystać z tej techniki refaktoryzacji?
Sprawdza się to szczególnie w sytuacji, kiedy możesz daną rzecz wykonać na wiele różnych sposobów. Kiedy masz wiele strategii, algorytmów, dróg, działań, reakcji... A jaką należy wybrać? To zależy... Wówczas ta metoda sprawdza się idealnie .
Na te różne sposoby by dojść do pewnego celu patrz jak na strategie . To są strategie, które możesz uskutecznić by wykonać jakieś konkretne działanie. Działanie jest jedno, ale sposób jego osiągnięcia różny i zależny od wielu czynników. Każda strategia doprowadzi cię do tego samego celu, ale one wszystkie są unikatowe.
Jak zastosować tę metodę?
Po pierwsze znajdź punkt elastyczności. Znajdź takie miejsce w kodzie, gdzie możesz być elastyczny co do tego jak dojdziesz do konkretnego miejsca, sposób, algorytm, strategia. Punkt w którym możesz tego dokonać na wiele sposobów, masz wiele dróg.
Po drugie wyodrębnij każdą strategię do dedykowanej klasy.
Po trzecie upewnij się, że każda z tych strategii (klas) należy do wspólnego API/kontraktu/interfejsu. A oznacza to tyle, by po prostu upewnić się, że wszystkie te strategie (klasy) posiadają metodę o tej samej nazwie (wspólna metoda). Pozwoli nam to, na wykorzystanie mocy polimorfizmu .
To czy stworzysz ten interfejs - faktycznie - i zobowiążesz owe klasy strategii do jego implementacji - zależy wyłącznie od ciebie i jest opcjonalne. Nie musisz tego robić, ale jeśli uważasz że to ma sens, masz tak wiele przeróżnych strategii i uważasz, że da ci to dużo dobrego i jest użyteczne - wtedy zrób to . W przeciwnym razie, jeśli strategii masz tylko kilka... Czy naprawdę potrzebujesz tego interfejsu? Ponieważ to nie ma tak naprawdę znaczenia. A warto utrzymywać wszystko w prostocie (KISS)
. Jeśli tego nie zrobisz nie czyni to z ciebie złego programisty czy architekta oprogramowania, ani trochę. To czy warto mieć ten interfejs czy nie - zależy od kontekstu
.
Po czwarte określ adekwatną strategię i pozwól jej zająć się zadaniem .
Dodatkowe informacje
Tworząc klasę strategii nazywaj ją adekwatnie do tego co ona robi, jakie jest jej zadanie. Dla przykładu RegistersLifetimeUser.
Klasy strategii dzielą wspólne API, więc bez względu na to którą strategię wybierzesz, możesz wywołać tę samą metodę. Wszystko dzięki polimorfizmowi.
Często by osądzić w kodzie która droga jest adekwatna w obecnej sytuacji wykorzystać można metodę lub klasę fabryki (factory method / factory class). I to ta klasa/metoda będzie odpowiedzialna za wyznaczenie adekwatnej drogi/algorytmu/strategii. To będzie jej odpowiedzialnością, a gdy już ją wyznaczy - wystarczy że ją stworzy i zwróci.
Gdy wiemy już którą strategię chcemy wykonać... wystarczy ją wykonać (wykorzystując wspólną metodę, interfejs).
Przykład
Rejestracja użytkownika. Cel jeden, ale dróg i sposobów na to może być wiele. Ponieważ możemy rejestrować zwykłego użytkownika, płatnego, vip i tak dalej... W poniższym przykładzie - regularnego użytkownika i użytkownika z wiecznym dostępem do treści.
<?php
// strategia rejestracji użytkownika z wiecznym dostępem do treści
class RegistersLifetimeMember {
public function handle() {
// pierwsza droga, rejestracja użytkownika z wiecznym dostępem do treści...
}
}
// strategia rejestracji regularnego użytkownika
class RegistersUser {
public function handle() {
// druga droga, rejestracja regularnego użytkownika...
}
}
// kontroler subskrypcji
class SubscriptionsController
{
// metoda odpowiedzialna za rejestrację użytkownika
public function store(Request $request)
{
// wybierz odpowiednią strategię i wykonaj ją
$this->getRegistrationStrategy($request)->handle(); // piękna potęga polimorfizmu
}
// metoda odpowiedzialna za wybranie adekwatnej strategii (factory method)
protected function getRegistrationStrategy(Request $request)
{
// wybieranie adekwatnej strategii w zależności od pewnych okoliczności...
if($request->plan == 'forever') {
// wybieram strategię rejestracji użytkownika z wiecznym dostępem do treści...
return new RegistersLifetimeMember;
}
// wybieram strategię rejestracji regularnego użytkownika
return new RegistersUser;
}
}
Zalety
Cała logika związana ze wszystkimi strategiami nie leży już w jednym miejscu (nieodpowiednim do tego), przygnieciona stertą zbędnych warunków i innym kodem. Teraz każda strategia ma swoją własną, dedykowaną klasę . Pozbyliśmy się zbędnych warunków, zastosowaliśmy się do dobrej praktyki, świetnego wzorca i wykorzystujemy potęgę polimorfizmu. Kod jest teraz czystszy, dużo lepiej skalowalny i ma znacząco poprawioną architekturę... Po prostu, lepszy kod
!
Podsumowując
Brzmi przytłaczająco? Skomplikowanie? A wcale takie nie jest . I mam nadzieję, że doszedłeś do tego samego wniosku po lekturze tego artykułu i przeanalizowaniu użytego w nim przykładu.
Pamiętaj, by określić, czy jest to technika refaktoryzacji kodu którą możesz użyć - zobacz czy możesz znaleźć punkt elastyczności w związku z wykonaniem pewnego zadania (w przykładzie z tego artykułu - jeden punkt elastyczności z dwiema strategiami). Wyodrębnij każdą strategię do osobnej klasy. W klasach tych zaimplementuj jeden wspólny interfejs. I ostatecznie użyj jakiejś fabryki (zazwyczaj metody), której zadaniem będzie wyznaczenie adekwatnej strategii a następnie jej użyj .
Potęga polimorfizmu znów w akcji . Piękna rzecz.
Kończąc
To już wszystko w temacie refaktoryzacji strategiami . Jeśli ten artykuł był dla ciebie jakkolwiek przydatny to proszę podziel się nim z innymi, udostępnij go
.
Proszę cię również o zostawienie komentarza, podziel się swoją opinią o tym artykule i tym temacie.
Zapraszam cię również do lektury innych moich artykułów, szczególnie tych związanych z refaktoryzacją kodu .
A tymczasem życzę ci dobrego dnia, bywaj !