W artykule przedstawię alternatywę dla użycia bloku if/else
oraz switch
. Kod napisany w ten sposób będzie przyjemny w odbiorze, dodawanie kolejnych warunków jak również ich modyfikacja też nie powinna stanowić problemu. Zachęcam do przeczytania, być może wdrożycie takie podejście w Waszych projektach.
Przykład wraz z kodem
Przyjmijmy, że musimy policzyć bonus/zniżkę na podstawie typu użytkownika. W tym celu utworzymy funkcję getBonus()
, która będzie liczyła nam końcowy wynik.
export const getBonus = (user) => { if (user.type == 'standard') { // logika return 10; } else if (user.type == 'premium') { // logika return 20; } else if (user.type == 'vip') { // logika return 30; } // logika default return 0; }
Wszystko wydaje się być oczywiste, przekazujemy typ usera, liczymy bonus i zwracamy wartość. Jeżeli typów klientów będzie więcej, pierwsze co przychodzi na myśl to użycie switch
zamiast if
oraz else
, co trochę poprawi czytelność. Kolejnym krokiem mogłoby być wyniesienie logiki do oddzielnych funkcji. Całość mogłaby wyglądać mniej więcej w ten sposób.
export const getBonus = (user) => { switch (user.type) { case 'standard': return getBonusStandardUserType(user); case 'premium': return getBonusPremiumUserType(user); case 'vip': return getBonusVipUserType(user); default: return getBonusDefaultUserType(user); } }
W ten sposób logika dla poszczególnego typu klienta znajduje się wewnątrz funkcji, łatwiej możemy przetestować funkcję i to, czy zwraca ona poprawne dane. Poprawiła się też czytelność kodu.
Spróbujmy jeszcze zmodyfikować funkcję, aby uniknąć bloku switch. Przenieśmy teraz kod wraz z funkcjami do obiektu, usuwając całkowicie switch
.
const conditions = { standard: getBonusStandardUserType, premium: getBonusPremiumUserType, vip: getBonusVipUserType, default: getBonusDefaultUserType };
Teraz możemy wywołać już funkcję korzystając z obiektu i właściwości, do której przypisana jest odpowiednia funkcja.
return conditions[user.type](user);
Żeby bardziej przybliżyć jak „działa” taki zapis, można przyjrzeć się poniższemu fragmentowi.
// zwracamy odpowiednia funkcje z logika const functionCondition = conditions[user.type]; return functionCondition (user);
Pozostała jeszcze jedna kwestia do rozwiązania, mianowicie obsługa przypadku, w którym przekażemy typ usera, dla którego nie jest zdefiniowana żadna logika, czyli w tym przypadku default.
Skorzystajmy w tym celu z in
, który sprawdzi czy dla danego obiektu żądana właściwość jest dostępna.
Więcej na temat wyrażenia in
znajdziemy tutaj: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in
(user.type in conditions) // true / false
Zapis taki zwróci nam true
albo false
, w zależności czy obiekt conditions posiada zdefiniowaną właściwość. Dla naszego przypadku będzie to zdefiniowana funkcja w zależności od typu klienta.
(user.type in conditions) ? conditions[user.type] : conditions.default)
Powyższy kod zapewni nam już obsługę zwrócenia odpowiedniej funkcji albo funkcji domyślnej – conditions.default
, w sytuacji gdy typ użytkownika nie ma zdefiniowanej logik – gdy właściwość nie występuje w obiekcie conditions
.
Pozostało nam jeszcze wywołanie zwróconej funkcji z odpowiednim argumentem. Zwróćcie proszę uwagę na nawiasy.
return ((user.type in conditions) ? conditions[user.type] : conditions.default)(user);
Całość funkcji mogłaby wyglądać następująco:
export const getBonus = (user) => { const conditions = { standard: getBonusStandardUserType, premium: getBonusPremiumUserType, vip: getBonusVipUserType, default: getBonusDefaultUserType }; return ((user.type in conditions) ? conditions[user.type] : conditions.default)(user); }
W mojej opinii kod taki wygląda bardziej czytelnie niż użycie wielu if
, else if
, else
lub bloku switch. Wybór zostawiam jednak Wam. Mimo wszystko mam nadzieję, że przyda Wam się ta wiedza i gdzieś zostanie użyta.
Źródła:
Obraz: https://unsplash.com/photos/JZyJxeCBtF4