3 potężne techniki kodowania w celu usunięcia niechlujnych warunków
Przedstawia trzy potężne techniki kodowania w celu optymalizacji i uproszczenia złożonych struktur warunkowych, poprawiając jakość i utrzymywalność kodu.
W tworzeniu oprogramowania często napotykamy na logikę kodu, która musi obsługiwać wiele scenariuszy. Jeśli nie zostaną one odpowiednio zarządzane, te logiki mogą łatwo przekształcić się w długie łańcuchy if-else lub ogromne instrukcje switch. Ten artykuł przedstawi kilka skutecznych technik optymalizacji tych struktur, poprawiając jakość i utrzymywalność kodu.
1. Programowanie defensywne: wczesny zwrot
Załóżmy, że rozwijamy system uwierzytelniania użytkowników, który musi sprawdzać różne statusy użytkownika przed zezwoleniem na dostęp:
Ten kod ma oczywiste problemy strukturalne. Używa głęboko zagnieżdżonych struktur if-else, co sprawia, że kod jest trudny do odczytania i utrzymania. W miarę wzrostu liczby sprawdzanych warunków, poziom wcięć w kodzie się zwiększa, tworząc tak zwany kod "kształtu strzałki". Logika obsługi błędów jest rozproszona na różnych poziomach zagnieżdżeń, co nie sprzyja jednolitemu zarządzaniu. Co ważniejsze, główna logika kodu — przypadek, w którym dostęp jest dozwolony — jest pochowana głęboko w wielu warstwach warunkowych osądów, co brak intuicyjności. Ten styl kodowania nie tylko zmniejsza czytelność kodu, ale także zwiększa ryzyko błędów i utrudnia rozwijanie kodu.
Możemy zoptymalizować ten kod za pomocą podejścia "wczesny zwrot":
Stosując strategię "wczesny zwrot", z powodzeniem zoptymalizowaliśmy oryginalną strukturę kodu.
Ta metoda przynosi kilka ulepszeń:
- Znacząco zmniejsza złożoność zagnieżdżenia kodu. Każde sprawdzenie warunku jest obsługiwane niezależnie, co czyni ogólną logikę jaśniejszą i bardziej zrozumiałą. Ta spłaszczona struktura nie tylko poprawia czytelność kodu, ale także znacznie zmniejsza trudności związane z utrzymaniem.
- Ta metoda optymalizacji osiąga scentralizowane zarządzanie logiką obsługi błędów. Natychmiast zwracając wyniki po każdym sprawdzeniu warunku, unikamy niepotrzebnego wykonania kodu, jednocześnie centralizując obsługę różnych scenariuszy błędów, czyniąc cały proces obsługi błędów bardziej zorganizowanym.
- Główna logika kodu — warunki umożliwiające dostęp — staje się bardziej wyróżniająca się. Ta struktura sprawia, że główny cel kodu jest natychmiast oczywisty, znacznie zwiększając wyrazistość i zrozumiałość kodu.
2. Metoda tablicy przeszukiwania
Często napotykamy scenariusze, w których należy zwrócić różne wyniki w zależności od różnych danych wejściowych. Jeśli nie zostaną one odpowiednio obsłużone, te logiki mogą łatwo przekształcić się w długie łańcuchy if-else lub ogromne instrukcje switch. Na przykład na platformie e-commerce musimy zwrócić odpowiednie opisy statusu na podstawie różnych statusów zamówienia:
To jest typowy scenariusz zwracania różnych wyników na podstawie różnych przypadków. W miarę zwiększania się liczby przypadków, instrukcje switch lub sądy if-else stają się rozbudowane. Dodatkowo, w tym scenariuszu, jeśli użytkownicy muszą przetłumaczyć te statusy na inne języki, wymagałoby to modyfikacji ciała funkcji lub dodawania nowych funkcji, co wiązałoby się z dużymi kosztami utrzymania.
W tym przypadku możemy użyć metody tablicy przeszukiwania w celu optymalizacji kodu:
Po pierwsze, za pomocą obiektu Map do przechowywania relacji mapującej pomiędzy statusami a opisami, kod staje się bardziej zwięzły. Umożliwiliśmy także łatwe przenoszenie opisów statusu do plików konfiguracyjnych, co zapewnia wygodę internacjonalizacji i dynamicznych aktualizacji. Kiedy dodawane są nowe statusy, nie musimy modyfikować rdzenia logiki kodu; wystarczy dodać odpowiednie pary klucz-wartość w konfiguracji.
3. Programowanie zorientowane na interfejs
Podczas rozwijania dużych systemów oprogramowania często musimy wspierać wielu dostawców usług lub moduły funkcjonalne. Możemy rozważyć użycie programowania zorientowanego na interfejs na etapie projektowania oprogramowania, aby ułatwić późniejsze rozszerzenia, eliminując tym samym złożoność licznych warunkowych osądów wynikających z twardego kodowania w złożonych systemach.
Załóżmy, że rozwijamy wielojęzyczny system tłumaczeń, który musi obsługiwać różnych dostawców usług tłumaczeń. Jeśli nie weźmiemy pod uwagę programowania zorientowanego na interfejs na etapie projektowania, późniejsze rozszerzenia będą bardzo trudne:
Implementacja ta używa prostej i prymitywnej struktury if-else do wyboru dostawców tłumaczeń, co sprawia, że kod jest trudny do utrzymania i rozszerzenia. W miarę dodawania nowych dostawców tłumaczeń w przyszłości, istniejący kod musi być modyfikowany, a w miarę zwiększania liczby wspieranych dostawców tłumaczeń, kod stanie się rozbudowany i trudny do utrzymania. Jednocześnie ta złożona metoda jest również trudna do testowania jednostkowego, ponieważ nie jest łatwo symulować różnych dostawców tłumaczeń.
Aby rozwiązać te problemy, możemy użyć programowania zorientowanego na interfejs do optymalizacji kodu. Programowanie zorientowane na interfejs to ważny sposób realizacji polimorfizmu, pozwalający różnym obiektom reagować różnie na tę samą wiadomość.
Proces implementacji:
- Zdefiniuj interfejs strategii tłumaczenia:
- Zaimplementuj ten interfejs dla każdego dostawcy tłumaczeń:
- Przebuduj klasę TranslationService, przekazując strategię jako parametr:
- Użyj zoptymalizowanego kodu:
Dzięki zdefiniowaniu interfejsu TranslationStrategy
i wprowadzeniu programowania zorientowanego na interfejs, zyskaliśmy następujące korzyści:
TranslationService
może używać różnych strategii tłumaczeń przy każdym wywołaniu.- Dodanie nowych dostawców tłumaczeń jest proste, wystarczy utworzyć nową klasę strategii i zaimplementować interfejs.
- Kod kliencki może elastycznie wybierać strategię do użycia przy każdym tłumaczeniu bez modyfikowania rdzenia logiki
TranslationService
. - Każda strategia tłumaczenia może być testowana niezależnie, co poprawia testowalność kodu.
- Unikanie utrzymywania stanu w
TranslationService
sprawia, że usługa staje się bardziej bezstanowa i bezpieczniejsza wątkowo.
Podsumowanie
Optymalizacja struktur instrukcji warunkowych to ważny środek poprawy jakości kodu. Trzy metody przedstawione w tym artykule — programowanie defensywne, metoda tablicy przeszukiwania oraz programowanie zorientowane na interfejs (połączone z polimorfizmem) — każda ma swoje odpowiednie scenariusze:
- Programowanie defensywne jest odpowiednie do obsługi wielu niezależnych warunków i skutecznie zmniejsza zagnieżdżenie kodu.
- Metoda tablicy przeszukiwania jest odpowiednia do obsługi wymagań reagujących różnie na różne przypadki, czyniąc kod bardziej zwięzłym i łatwiejszym do utrzymania.
- Programowanie zorientowane na interfejs, połączone z polimorfizmem, jest odpowiednie do budowania złożonych, ale elastycznych systemów, poprawiając elastyczność i skalowalność kodu.
W rzeczywistym rozwoju często musimy wybierać odpowiednie metody w oparciu o konkretne sytuacje, a czasem nawet musimy kompleksowo stosować wiele technik. Ważne jest, aby zachować równowagę między prostotą, czytelnością i utrzymywalnością kodu, wybierając rozwiązanie, które najlepiej pasuje do aktualnego problemu.
Pamiętaj, że nadmierna optymalizacja może prowadzić do nadmiernie złożonego kodu. Utrzymywanie kodu prostym i czytelnym jest zawsze główną zasadą. Podczas stosowania tych technik należy podejmować mądre decyzje w oparciu o konkretne potrzeby projektu i poziom techniczny zespołu.