Mam aplikację, która przyjmuje liczbę całkowitą jako dane wejściowe i na podstawie wejściowych wywołań metod statycznych różnych klas. Za każdym razem, gdy dodawany jest nowy numer, musimy dodać kolejny przypadek i wywołać inną metodę statyczną innej klasy. W przełączniku jest teraz 50 skrzynek i za każdym razem, gdy muszę dodać kolejną skrzynkę, wzdrygam się. Czy jest na to lepszy sposób?
Zastanowiłem się i wpadłem na ten pomysł. Używam wzorca strategii. Zamiast mieć skrzynkę przełączników, mam mapę obiektów strategii z kluczem będącym liczbą całkowitą wejściową. Po wywołaniu metody będzie ona wyszukiwać obiekt i wywoływać ogólną metodę dla obiektu. W ten sposób mogę uniknąć użycia konstrukcji skrzynki przełączników.
Co myślisz?
design-patterns
object-oriented-design
Kaushik Chakraborty
źródło
źródło
switch
przypadek i wywołać wcześniej istniejącą metodę w złożonym systemie, czy też musisz wymyślić zarówno metodę, jak i jej wywołanie?Odpowiedzi:
Uwielbiam polimorfizm. Uwielbiam SOLID. Uwielbiam programowanie czysto obiektowe. Nienawidzę patrzeć na tych złych przedstawicieli, ponieważ są stosowane dogmatycznie.
Nie zrobiłeś dobrego argumentu za refaktoryzacją strategii. Nawiasem mówiąc, refaktoryzacja ma swoją nazwę. Nazywa się to Zamień warunkowy na polimorfizm .
Znalazłem dla ciebie odpowiednie porady z c2.com :
Masz przełącznik z 50 skrzyniami, a twoją alternatywą jest wyprodukowanie 50 przedmiotów. Aha i 50 wierszy kodu konstrukcji obiektu. To nie jest postęp. Dlaczego nie? Ponieważ to refaktoryzacja nie wpływa na zmniejszenie liczby z 50. Korzystasz z tego refaktoryzacji, gdy okazuje się, że musisz utworzyć kolejną instrukcję switch na tym samym wejściu w innym miejscu. Właśnie wtedy refaktoryzacja pomaga, ponieważ zamienia 100 z powrotem na 50.
Tak długo, jak mówisz o „przełączniku”, jakby to był jedyny, nie polecam tego. Jedyną korzyścią wynikającą z refaktoryzacji jest to, że zmniejsza ona szanse, że niektóre goofball skopiują i wkleją przełącznik 50 skrzynek.
Polecam uważnie przyjrzeć się tym 50 przypadkom pod kątem podobieństw, które można uwzględnić. Mam na myśli 50? Naprawdę? Jesteś pewien, że potrzebujesz tylu skrzynek? Być może próbujesz tutaj wiele zrobić.
źródło
Mapa samych obiektów strategii, która jest inicjowana w jakiejś funkcji kodu, w której wygląda kilka linii kodu
wymaga od ciebie i twoich współpracowników zaimplementowania funkcji / strategii, które mają być wywoływane w oddzielnych klasach, w bardziej jednolity sposób (ponieważ wszystkie obiekty strategii będą musiały implementować ten sam interfejs). Taki kod jest często nieco bardziej kompleksowy niż
Jednak nadal nie zwolni cię to z obowiązku edytowania tego pliku kodu, ilekroć trzeba dodać nowy numer. Rzeczywiste zalety tego podejścia są inne:
inicjalizacja mapy jest teraz oddzielona od kodu wysyłki, który faktycznie wywołuje funkcję powiązaną z określoną liczbą, a ta ostatnia nie zawiera już tych 50 powtórzeń, po prostu będzie wyglądać
myMap[number].DoIt(someParameters)
. Dlatego ten kod wysyłki nie musi być dotykany za każdym razem, gdy nadejdzie nowy numer, i można go wdrożyć zgodnie z zasadą Otwarte-Zamknięte. Co więcej, gdy otrzymasz wymagania, w których musisz rozszerzyć sam kod wysyłki, nie będziesz już musiał zmieniać 50 miejsc, ale tylko jedno.zawartość mapy jest określana w czasie wykonywania (podczas gdy zawartość konstrukcji przełącznika jest określana przed czasem kompilacji), więc daje to możliwość uczynienia logiki inicjalizacji bardziej elastyczną lub rozszerzalną.
Tak, są pewne korzyści, a to z pewnością krok w kierunku bardziej SOLIDNEGO kodu. Jeśli jednak opłaca się refaktoryzować, jest to coś, co Ty lub Twój zespół będzie musiał sam podjąć. Jeśli nie spodziewasz się, że kod wysyłki zostanie zmieniony, logika inicjalizacji zostanie zmieniona, a czytelność
switch
nie jest prawdziwym problemem, wówczas refaktoryzacja może nie być teraz tak ważna.źródło
Doit1
,Doit2
itp z jednejDoit
metody, która ma wiele różnych implementacji.doTheThing()
metodę symbolu wejściowego. Więc nie musisz utrzymywać mapy.Jestem zdecydowanie za strategią przedstawioną w odpowiedzi @DocBrown .
Mam zamiar zasugerować poprawę odpowiedzi.
Połączenia
może być dystrybuowany. Nie musisz wracać do tego samego pliku, aby dodać kolejną strategię, która jeszcze lepiej przestrzega zasady Open-Closed.
Załóżmy, że implementujesz
Strategy1
w pliku Strategy1.cpp. Możesz mieć następujący blok kodu.Możesz powtórzyć ten sam kod w każdym pliku StategyN.cpp. Jak widać, będzie to dużo powtarzającego się kodu. Aby zmniejszyć duplikację kodu, możesz użyć szablonu, który można umieścić w pliku dostępnym dla wszystkich
Strategy
klas.Następnie jedyną rzeczą, której musisz użyć w Strategy1.cpp, jest:
Odpowiednia linia w StrategyN.cpp to:
Możesz przenieść szablony na inny poziom, używając szablonu klas dla konkretnych klas strategii.
I zamiast tego
Strategy1
użyjConcreteStrategy<1>
.Zmień klasę pomocnika, aby zarejestrować
Strategy
s:Zmień kod w Strateg1.cpp na:
Zmień kod w StrategN.cpp na:
źródło