Zacznij Black Week i oszczędzaj już teraz!

dodatkowe -15% na wszystkie Pakiety z kodem bundle2024

Nie zawsze wyjątek jest najlepszym rozwiązaniem

Wyjątki to wspaniały mechanizm, który pozwala zareagować na sytuacje, które uniemożliwiają funkcji wykonanie przeznaczonego jej zadania. Czasami jednak wyjątek, nie jest optymalnym wyjściem z sytuacji. Czasami istnieje lepsze rozwiązanie.

Poprawne dane pomimo sytuacji wyjątkowej

Jeśli wiemy, jak zostaną wykorzystane dane wynikowe zwrócone przez funkcje, możemy pomimo wystąpienia błędu, zwrócić dane, które nadal będą poprawne.
Spójrzmy na przykład z poprzedniego artykułu:

Cel istnienia funkcji get_wpdesk_post_title jest jasny, ma ona pobrać tytuł posta. Jeśli do jej odpowiedzialności dodamy poprawną reakcję na zaistnienie błędu, to możemy zmodyfikować jej zachowanie.

Co zyskaliśmy dzięki temu? W funkcji display_wpdesk_title już nie wystąpi wyjątek – stała się łatwiejsza w obsłudze. Końcowy kod, którego zadaniem jest wyświetlenie tytułu posta, jest jeszcze łatwiejszy – nie musi już brać pod uwagę sytuacji wyjątkowej.

Czy coś straciliśmy? Tak. Oddaliśmy decyzję, jak będzie wyglądał tytuł nieistniejącego posta funkcji display_wpdesk_title. To znaczy, że cały kod, który jest od niej zależny, nie będzie już mógł sam zadecydować jak postąpić w przypadku wystąpienia błędu. Rozwiązania, które w tym celu mogą próbować podejścia if ($result === 'Some placeholder title') pominę milczeniem.

Jest jeszcze jeden potencjalny problem, który w tym momencie nie jest jeszcze wyraźnie widoczny. Scedowaliśmy do funkcji display_wpdesk_title reakcję na brak posta, ale w kodzie innego modułu np. odpowiadającego za wyświetlenie treści posta, inna funkcja będzie musiała zareagować na taką samą sytuację wyjątkową. Może się okazać, że sposób reakcji na ten problem będzie w przyszłości rozproszony po całym programie w różnych funkcjach i stracimy pojedyncze źródło prawdy. Nie będzie jednego miejsca, gdzie będzie można zmodyfikować zachowanie posta który nie istnieje. Reakcja na brak posta może też w pewnym momencie stać się niespójna, a nawet prowadzić do sprzecznych zachowań w programie.

Przykład ze świata obiektowego

Zanim spróbujemy ulepszyć kod jeszcze bardziej, zapomnijmy na chwilę o WordPress. Istnieje wzorzec projektowy, którego zadaniem jest rozwiązanie opisanego tutaj problemu. Jest to obiekt specjalnego przeznaczenia, tak zwany Pusty obiekt (ang. NullObject). Żeby go skutecznie zastosować, powinniśmy mieć możliwość implementacji interfejsu dla klasy, dla której chcemy dodać “puste zachowanie”.
Rozważmy następujący przypadek. Chcemy w naszym programie logować błędy. W tym celu chcielibyśmy utworzyć funkcję get_logger która w wyniku zwróci nam obiekt, którego zadaniem jest logowanie błędów w odpowiednim miejscu. Na przykład błędy krytyczne logowane są w pliku, ale poza tym wysyłane na kanał Slacka, a normalne błędy logowane są do pliku, a poza tym wysyłane mailem.

Funkcja get_logger zwraca obiekt, który implementuje interfejs LoggerInterface. To prosty polimorfizm. Użytkownik funkcji nie wie jaką implementację obiektu otrzyma i nie ma to dla niego znaczenia. Znaczenie ma pewność, którą daje nam interfejs, czyli, że obiekt będzie miał publiczne metody zadeklarowane w LoggerInterface. Znaczenie ma to, że wynikowy obiekt będzie umiał logować.
Dzięki takiemu podejściu rozwiązanie polega na abstrakcji czyli na pojęciu czym w istocie jest funkcjonalność logowania, a nie jak dokładnie została ona zaimplementowana.

Kod oparty o abstrakcje lepiej reaguje na zmiany i wprowadza mniej zależności.

Jeśli jest tak dobrze, to dlaczego jest tak źle?

Kod wygląda dobrze, wszystko ładnie się loguje, a na dedykowanym kanale Slacka pojawiają się ważniejsze problemy. Jak jednak wyglądałby ten kod, gdybyśmy pozwolili funkcji get_logger na zwrotkę nulla albo rzucanie wyjątków? Przecież logger jest dość skomplikowany. Slack może mieć problem z API, plik z logami może nie mieć uprawnień do zapisu, serwer SMTP może nie być dostępny. Jeśli dopuścimy sytuację, że get_logger rzuca wyjątek, albo – co gorsza – zwraca nulla, to ten prosty kod zmieni się w koszmar. Każde wywołanie get_logger trzeba będzie opakować w try/catch. Możemy oczywiście użyć zmiennej pomocniczej, ale najprawdopodobniej to nie jest jedyne miejsce w kodzie, gdzie będziemy używać loggera. Przecież nie chcemy go przekazywać jako parametru do każdej funkcji w programie.

Wzorzec projektowy NullObject na ratunek

Dlatego, w przypadku gdy klasa logująca nie może zostać poprawnie utworzona, wykorzystamy obiekt, który zaimplementuje pustą funkcjonalność.

Pięknie i prosto. Teraz jeśli get_logger ma problem, to reszta programu nie musi o tym wiedzieć. W takiej sytuacji nic nie zostanie zapisane do logów, ale program nadal będzie działał. Oczywiście warto jakoś zareagować na fakt, że nie mamy możliwości logowania, jednak to nie jest problem get_logger. Tym bardziej nie jest to zadanie funkcji display_wpdesk_title.

Wracamy do WordPress

Uzbrojeni w nową wiedzę możemy wrócić do pierwotnego problemu z obsługą błędów w tytule posta. Ponieważ jest to WordPress to mamy pewien problem.

Programowanie z użyciem obiektów, to nie jest programowanie obiektowe.

WordPress nie jest napisany obiektowo, więc klasa WP_Post nie tylko nie implementuje żadnego interfejsu, który moglibyśmy rozszerzyć. Klasa nie rozszerza też innej klasy, a dodatkowo jest zadeklarowana ze słowem kluczowym final (co jest bardzo dobrym pomysłem). Niestety słowo kluczowe final powoduje, że nie możemy w żaden sposób rozszerzyć także WP_Post. Nie możemy więc skorzystać z polimorfizmu. Dlatego jeśli chcemy stworzyć WPNullPost, to musimy odwołać się do sprytu.

Dzięki takiemu rozwiązaniu udało się zminimalizować wspomniane wcześniej wady get_wpdesk_post_title. Teraz istnieje jedno miejsce, które gromadzi wiedzę o tym, jak reprezentujemy brakujący post, a kod jeszcze lepiej oddaje intencje.

Mam nadzieję, że artykuł trochę przybliżył problemy, które rozwiązujemy w ramach Review w WP Desk. Dajcie znać w komentarzach, czy chcecie wiedzieć jeszcze więcej o naszych praktykach pracy! :)

Zobacz inne artykuły

Dołącz do ponad 10 000 czytelników

Poradniki WooCommerce i nowości –
wprost na Twój e-mail.

Preferencje plików cookies

Inne

Inne niekategoryzowane pliki cookie to te, które są analizowane i nie zostały jeszcze przypisane do żadnej z kategorii.

Niezbędne

Niezbędne
Niezbędne pliki cookie są absolutnie niezbędne do prawidłowego funkcjonowania strony. Te pliki cookie zapewniają działanie podstawowych funkcji i zabezpieczeń witryny. Anonimowo.

Reklamowe

Reklamowe pliki cookie są stosowane, by wyświetlać użytkownikom odpowiednie reklamy i kampanie marketingowe. Te pliki śledzą użytkowników na stronach i zbierają informacje w celu dostarczania dostosowanych reklam.

Analityczne

Analityczne pliki cookie są stosowane, by zrozumieć, w jaki sposób odwiedzający wchodzą w interakcję ze stroną internetową. Te pliki pomagają zbierać informacje o wskaźnikach dot. liczby odwiedzających, współczynniku odrzuceń, źródle ruchu itp.

Funkcjonalne

Funkcjonalne pliki cookie wspierają niektóre funkcje tj. udostępnianie zawartości strony w mediach społecznościowych, zbieranie informacji zwrotnych i inne funkcjonalności podmiotów trzecich.

Wydajnościowe

Wydajnościowe pliki cookie pomagają zrozumieć i analizować kluczowe wskaźniki wydajności strony, co pomaga zapewnić lepsze wrażenia dla użytkowników.