Mój szef dał mi projekt ze szczególną logiką. Muszę opracować stronę internetową, która musi prowadzić nawigatora przez wiele przypadków, aż dotrze on do produktu.
Oto schemat ścieżki nawigacji na stronie:
WAŻNY!
Na stronie Produkty nawigator może wybrać filtr, który chce.
- Jeśli A, MUSI przejść przez B (a następnie oczywiście C) lub C i dotrzeć do produktów.
- Jeśli B, MUSI przejść przez C i dotrzeć do produktów.
- Jeśli C, on / ona dociera bezpośrednio do produktów.
Oczywiście, jeśli zacznę od sztucznej inteligencji, podążę najdłuższą ścieżką, a kiedy dotrę do moich produktów, mam 3 aktywne filtry.
Do tej pory opracowałem następujący kod, który działa dobrze.
if filter_A
if filter_B
filter_C()
.. else ..
else
filter_C
.. else ..
else
if filter_B
filter_C()
.. else ..
else
filter_C()
.. else ..
Jestem tutaj, aby zapytać, co zrobiłby bardziej doświadczony programista w tej sytuacji. Nie przestrzegałem zasady DRY, nie podoba mi się to i chciałbym poznać alternatywny sposób na rozwinięcie tego rodzaju logiki.
Myślałem o podzieleniu każdej sekcji kodu na funkcje, ale czy w tym przypadku to dobry pomysł?
źródło
filter_C
, ale instrukcje warunkowe wskazują, że przepływ sterowania może się zmieniaćfilter_C
. Jestfilter_C
opcjonalny?Odpowiedzi:
Nie powiedziałeś, czy filtry przyjmują jakieś parametry. Na przykład
filter_A
może to być filtr kategorii, aby nie było to tylko pytanie „czy muszę zastosowaćfilter_A
”, może to być „muszę zastosowaćfilter_A
i zwrócić wszystkie rekordy z polem kategorii =fooCategory
”.Najprostszy sposób na wdrożenie dokładnie tego, co opisałeś (ale koniecznie przeczytaj drugą połowę odpowiedzi poniżej) jest podobny do innych odpowiedzi, ale w ogóle nie miałbym żadnych czeków logicznych. Chciałbym określić interfejsy:
FilterA, FilterB, FilterC
. Wtedy możesz mieć coś takiego (jestem programistą Java, więc będzie to składnia w stylu Java):Wtedy możesz mieć coś takiego (używając wzorca singleton enum z Effective Java ):
Ale jeśli faktycznie chcesz filtrować niektóre elementy, możesz zamiast tego podać instancję
FilterA
implementacji, która faktycznie coś robi. Twoja metoda filtracji będzie bardzo prostaAle dopiero zaczynam.
Podejrzewam, że
applyFilter
połączenie będzie w rzeczywistości bardzo podobne dla wszystkich trzech rodzajów filtrów. W takim przypadku nawet nie zrobiłbym tego w sposób opisany powyżej. Możesz uzyskać jeszcze czystszy kod, mając tylko jeden interfejs, a następnie robiąc to:Następnie, gdy użytkownik porusza się po stronach, po prostu dodajesz nową instancję dowolnego filtra, którego potrzebujesz, w razie potrzeby. Umożliwi to stosowanie wielu wystąpień tego samego filtra z różnymi argumentami, jeśli będziesz potrzebować takiego zachowania w przyszłości, a także dodawanie dodatkowych filtrów w przyszłości bez konieczności zmiany projektu .
Dodatkowo możesz dodać coś takiego jak
NoOpFilter
wyżej lub po prostu nie możesz w ogóle dodać konkretnego filtru do listy, cokolwiek jest łatwiejsze dla twojego kodu.źródło
Filter
jako,Predicate
możesz użyć go bezpośrednio wStream
interfejsie API. Wiele języków ma podobne konstrukcje funkcjonalne.W takim przypadku ważne jest, aby oddzielić logikę filtrowania i przepływ kontrolny działania filtrów. Logika filtrowania powinna być podzielona na poszczególne funkcje, które mogą działać niezależnie od siebie.
W przykładowym kodzie pisał, tam 3 wartości logicznych
filter_A
,filter_B
orazfilter_C
. Jednak z diagramufilter_C
zawsze działa, więc można go zmienić na bezwarunkowy.UWAGA: Zakładam, że schemat kontroli jest prawidłowy. Występuje rozbieżność między wysłanym kodem próbki a diagramem kontroli.
Osobny fragment kodu kontroluje uruchamianie filtrów
Istnieje wyraźny rozdział między kontrolowaniem, które filtry działają, a tym, co robią filtry. Rozbij te dwa elementy logiki.
źródło
Zakładam, że chcesz najprostszego, najczystszego algorytmu.
W tym przypadku, wiedząc, że filtr c jest zawsze stosowany, chciałbym przeżyć go poza logiką if i zastosować go na końcu, niezależnie od tego. Jak widać na schemacie blokowym, każdy filtr przed literą c jest opcjonalny, ponieważ każdy z nich można zastosować lub nie. W takim przypadku żyłbym, gdyby oddzielne od każdego filtra, bez zagnieżdżania i łączenia:
jeśli masz schemat blokowy ze zmienną liczbą filtrów, przed obowiązkowym, zapisałbym wszystkie filtry w tablicy, w kolejności, w jakiej powinny się pojawić. Następnie przetworz opcjonalne filtry w pętli i zastosuj obowiązkowy na końcu, poza pętlą:
lub:
oczywiście, musisz zdefiniować podprogram przetwarzania filtrów.
źródło
Zakładam, że filterA, filterB i filterC faktycznie modyfikują listę produktów. W przeciwnym razie, jeśli są one tylko sprawdzeniami typu if, wtedy filterA i filterB można zignorować, ponieważ wszystkie ścieżki prowadzą ostatecznie do filterC. Twój opis wymagania wydaje się sugerować, że każdy filtr zmniejszy listę produktów.
Więc zakładając, że filtry faktycznie zmniejszają listę produktów, oto trochę pseudo-kodu ...
W twoich wymaganiach filterC nie jest stosowane automatycznie, ale na schemacie tak jest. Jeśli wymaganie jest takie, że przynajmniej filtr C powinien być zastosowany bez względu na wszystko, wtedy wywołałbyś ApplyFilter (filterC, produkty) bez sprawdzania, czy wybrano filterC.
źródło
Zastanawiam się, czy modelowanie filtrów, aby były jakimiś obiektami na wykresie, miałoby sens. Przynajmniej o tym myślę, widząc schemat.
Jeśli modelujesz zależność filtrów jak wykres obiektowy, to kod, który obsługuje możliwe ścieżki przepływu, jest dość prosty, bez jakiejkolwiek kosmicznej logiki. Również wykres (logika biznesowa) może się zmieniać, a kod interpretujący wykres pozostaje taki sam.
źródło