Development / DevPark / Laravel

Wprowadzenie

W mojej pracy bardzo często jestem odpowiedzialny za przeglądanie kodu innych programistów lub doprowadzam do końca projekty rozpoczęte przez inne zespoły programistyczne.

To co bardzo często mam wątpliwą przyjemność zauważyć, to zaszyte na sztywno parametryzowanie kodu, które często jest przyczyną poważnych problemów w pracy z takim kodem kiedy projekt się rozrasta oraz pracuje nad nim coraz większa ilość osób.

Używanie liczb oraz wartości tekstowych

Pierwszy przypadek jest bardzo “popularny” i polega na tym, że często w kodzie można znaleźć tego typu kawałki:

 

Przy pierwszym spojrzeniu problem nie jest zauważalny, ale jeśli się zastanowisz troszkę….. czym w tym miejscu jest  admin ? To zwykły tekst!! Jesteś pewien, że ta rola to teraz i zawsze będzie nazywać się “admin”? Co jeśli jutro ktoś w bazie zmieni tę wartość np. na “administrator” bo np. będzie chciał ją bezpośrednio wyświetlać w kodzie html?

Inna sprawa – zakładając, że taki sposób użycia jest w wielu miejscach kodu (pomijamy tu już zasadę DRY) bardzo łatwo można popełnić literówkę która zostanie przeoczona (i potem trzeba jej szukać), jesli kod nie jest jakimś kluczowym elementem i przypadkiem pominiemy testy.

 

Rozwiązanie powyższego zagadnienia jest wręcz banalne – zamiast używać wartości tekstowych zawsze używaj stałych (const) zdefiniowanych w specjalnej klasie. Dla przykładu do użycia w klasie User możesz utworzyć klasę RoleType i użyć jej tak:

Teraz zamiast sztywno wpisanego tekstu, możesz używać wszędzie stałej. Są dwie podstawowe zalety takiego rozwiązania.

pierwsza – jeśli ktoś zdecyduje się zmienić nazwę roli w bazie, zmieniasz jej wartość tekstową tylko w jednym miejscu (w RoleType):

Druga – trudniej zrobić literówkę, ponieważ twoje IDE wskaże Ci taki błąd (zakładam że uzywasz podpowiedzi kontekstowych w swoim IDE).

Sztywno podawanie tablic

Podobna sytuacja ma miejsce w momencie kiedy podajemy sztywno zapisane tablice. Rozważmy taki przypadek:

To oczywiście bardzo uproszczony przypadek, ale obrazuje problem  gdzie jako jeden z parametrów została podana utworzona w locie tablica ról. Takie podanie tablicy [RoleType::ADMIN, RoleType::USER]  nie jest jasne dla innych programistów, ponieważ nie wiedzą jaka jest intencja, że właśnie takie role a nie inne musiały zostać użyte.  Prawdopodobnie chciałeś tu podać wszystkie role jakie są?

Załóżmy teraz, że aplikacja w której użyty został taki kod zawiera skomplikowaną logikę. Do projektu wchodzi nowy developer i ma za zadanie dodać nową rolę w systemie. Czy dla niego będzie to jasne, że w takich miejscach ta rola ma też zostać dodana? A może w jednych tak (bo chcemy wszystkie role) a w innych nie (bo chcemy tylko te dwie konkretne)?

 

Tak więc po dodaniu stałej w RoleType będzie się mocno zastanawiać co tutaj zrobić….

Prawdopodobnie będzie zmuszony do przeanalizowania całej logiki kodu który korzysta z tego oraz przeanalizować wszystkie zależności…

Rozwiązaniem tego problemu jest rozpoczęcie używania metod, których nazwa jasno określa co jest robione, dla przykładu

I teraz w zależnosci od logiki kodu możemy użyc:

lub

W takim kodzie, dla programisty jest jesne, że nowo dodana rola powinna zostać dodana wyłącznie w metodzie all().

Oczywiście są przypadki kiedy zastosowanie tego nie jest takie oczywiste na początki – ale w miarę rozwoju aplikacji powinno się o takie elementy dbać.

Użycie sztywno przypisanych identyfikatorów liczbowych

Prawdopodobnie najgorszy przypadek lecz nadal często spotykany w użyciu. Przykładowy zestaw danych:

#  Id # Description          #

==============================

#  1  # System administrator #

#  2  # User                 #

#  3  # Manager              #

I teraz przykładowy kod, którego zadaniem jest wyszukanie wszystkich administratorów systemu:

Wykorzystana zostaje tu kolumna odwołująca się w relacji do ról systemu. Jaki jest problem?

Cóż – są ty właściwie dwa problemy.

Pierwszy – ponownie używana jest na sztywno wpisana wartość. Nie jest już tekstem a liczbą – ale czy masz pewność, że dla każdego będzie jasne, że ta konkretna liczba to jest dana rola? co jeśli ktoś popełni literówkę i zamiast 1 wpisze 12?

Druga bardziej problematyczna spraw – czy masz pewność, że rola administratora zawsze będzie miała id =1? Co jeśli w innej instancji bazy będzie to inna wartość? Przykładowo wyobraźmy sobie sytaucję, że podczas wypuszczania kodu na produkcję wystąpi jakiś wyjątek i podczas dodawania ról jedna z nich nie zostanie dodana. Wartości id będą wówczas inne niż te zapisane na sztywno!!

Rozwiązanie jest na szczęście proste. Nigdy nie polegaj na ID w przypadku takich obiektów. Zamiast tego używaj unikatowych synonimów (slug) – dla przykładu:

#  Id # Slug    #  Description         #

========================================

#  1  # admin   # System administrator #

#  2  # user    # User                 #

#  3  # manager # Manager              #

so as you see additional slug column was added into database.

Klasa z typami ról może wyglądać następująco

a jej użycie zamiast tego:

powinno wyglądać tak:

Co prawda wydaje się to nieco więcej kodu, ale jest on teraz łatwo zarządzalny oraz skalowalny. Można by go nieco jeszcze nieco ulepszyć, poprzez stworzenie metody pomocniczej, która skróci nieco składnię:

Gdzie byRole() jest metodą, w która zawiera już właściwy kod wywołujący zapytanie do bazy danych.

autor: Marcin Nabiałek, leading programmer at Devpark Laravel team