„Mam nieprzespane noce, próbując dodać funkcje do kodu, który przejęliśmy od innej firmy. Mam do czynienia z najczystszą formą Legacy Code”

„Naprawdę ciężko mi radzić sobie z poplątanym, nieustrukturyzowanym kodem, z którym muszę pracować, ale którego ani trochę nie rozumiem. Legacy Code !”

Legacy Code to termin, który prawdopodobnie ma wiele różnych definicji, takich jak – kod przejęty od kogoś innego, kod napisany przez kogoś innego, kod, który jest trudny do zrozumienia lub kod napisany w przestarzałych technologiach. Niezależnie od definicji, większość z nas uważa, że legacy code jest przerażający.

Pytanie>Jak zdefiniowałbyś legacy code?

Definiowanie legacy code

Michael Feathers w swojej książce „Working Effectively with Legacy Code” definiuje legacy code jako, kod bez testów.

Kod bez testów jest złym kodem. Nie ma znaczenia jak dobrze jest napisany; jak dobrze jest ustrukturyzowany; jak dobrze jest enkapsulowany. Bez testów nie ma sposobu, aby powiedzieć, czy nasz kod staje się lepszy czy gorszy.

Cóż, nieco zmodyfikowana wersja tej definicji to „kod bez testów jednostkowych jest nazywany kodem dziedzictwa”. Zawsze lepiej jest mieć testy tak blisko kodu jak to tylko możliwe (testy jednostkowe > testy integracyjne > testy UI). Tak więc, nie byłoby niesprawiedliwe nazwanie kodu bez testów jednostkowych kodem starości.

Praca z kodem starości

Pytanie>Jakie podejście przyjmiesz, jeśli miałbyś dokonać zmiany w kodzie starości?

Większość z nas może powiedzieć: „Zrobię zmianę i na tym koniec, po co zawracać sobie głowę poprawianiem kodu”. Uzasadnieniem tego procesu myślowego może być –

  • Nie mam wystarczająco dużo czasu, aby refaktoryzować kod, Wolałbym wprowadzić zmianę i dokończyć moją historię
  • Po co ryzykować zmianę struktury kodu, który działa w produkcji od dłuższego czasu
  • Jaka jest ogólna korzyść z refaktoryzacji starszego kodu

Michael Feathers nazywa ten styl wprowadzania zmian jako Edit and Pray. Planujesz i wprowadzasz swoje zmiany, a kiedy skończysz, modlisz się i modlisz się mocniej, aby twoje zmiany były właściwe.

Z tym stylem można tylko przyczynić się do zwiększenia Legacy code.

Istnieje inny styl wprowadzania zmian, którym jest Cover and Modify. Zbuduj siatkę bezpieczeństwa, wprowadź zmiany w systemie, pozwól siatce bezpieczeństwa dostarczyć informacje zwrotne i pracuj nad tymi informacjami zwrotnymi.

Można bezpiecznie założyć, że Cover and Modify jest sposobem na radzenie sobie ze starszym kodem.

Pytanie> Ale, czy w ogóle powinieneś poświęcać czas na pisanie testów w starszym kodzie lub nawet myśleć o refaktoryzacji starszego kodu?

Czy powinieneś w ogóle poświęcać czas na myślenie o refaktoryzacji Legacy Code ?

The Boy Scout Rule

Idea stojąca za Boy Scout Rule, jak stwierdził wujek Bob, jest dość prosta: Zostaw kod czystszy niż go znalazłeś! Kiedykolwiek dotykasz starego kodu, powinieneś go odpowiednio wyczyścić. Nie należy stosować rozwiązań na skróty, które utrudnią zrozumienie kodu, ale traktować go z należytą starannością. Nie wystarczy dobrze napisać kod, kod musi być utrzymywany w czystości przez długi czas.

Uzyskujemy bardzo silny przekaz, gdy zasada harcerska jest stosowana do starszych kodów „zostaw za sobą ślad zrozumienia dla innych, aby mogli podążać”, co oznacza, że będziemy refaktoryzować kod, aby uczynić go bardziej zrozumiałym. A żeby refaktoryzować, zbudujemy wokół niego Safety Net.

Teraz, gdy rozumiemy, że nie możemy iść na skróty, jedyną opcją, jaka nam pozostała, jest napisanie kilku testów, refaktoryzacja kodu i kontynuowanie rozwoju. Pytania>

  • Które testy powinniśmy napisać?
  • Jak dużo powinniśmy refaktoryzować?

Które testy napisać

W prawie każdym starszym systemie, to co system robi jest ważniejsze niż to co ma robić.

Testy charakteryzujące, testy, których potrzebujemy, gdy chcemy zachować zachowanie nazywane są testami charakteryzującymi. Test charakterystyki jest testem, który charakteryzuje rzeczywiste zachowanie fragmentu kodu. Nie ma „Cóż, to powinno robić to” lub „Myślę, że to robi tamto”. Testy dokumentują rzeczywiste bieżące zachowanie systemu.

Pisanie Testu Charakteryzacji

Test Charakteryzacji z definicji dokumentuje rzeczywiste bieżące zachowanie systemu dokładnie w taki sam sposób, w jaki działa on na środowisku produkcyjnym.

Napiszmy test charakterystyki dla obiektu Customer, który generuje tekstowe oświadczenie dla niektórych filmów wypożyczonych przez klienta.

import static com.code.legacy.movie.MovieType.CHILDREN;
import static org.junit.Assert.assertEquals;public void shouldGenerateTextStatement(){ Customer john = new Customer("John");
Movie childrenMovie = new Movie("Toy Story", CHILDREN);
int daysRented = 3;
Rental rental = new Rental(childrenMovie, daysRented); john.addRental(rental); String statement = john.generateTextStatement();
assertEquals("", statement);
}

Test ten próbuje zrozumieć (lub scharakteryzować) generowanie „Oświadczenia tekstowego” dla klienta, który otrzymał film dla dzieci wypożyczony na 3 dni. Ponieważ nie rozumiemy systemu (przynajmniej jak na razie), spodziewamy się, że oświadczenie będzie puste lub będzie zawierało dowolną wartość fikcyjną.

Uruchommy test i pozwólmy mu zawieść. Kiedy tak się stanie, dowiedzieliśmy się, co tak naprawdę robi kod pod tym warunkiem.

java.lang.AssertionError: 
Expected :""
Actual :Rental Record for John, Total amount owed = 12.5. You earned 4 frequent renter points.

Teraz, gdy znamy zachowanie kodu, możemy iść dalej i zmienić test.

import static com.code.legacy.movie.MovieType.CHILDREN;
import static org.junit.Assert.assertEquals;public void shouldGenerateTextStatement(){
String expectedStatement = "Rental Record for John, Total amount owed = 12.5. You earned 4 frequent renter points"; Customer john = new Customer("John");
Movie childrenMovie = new Movie("Toy Story", CHILDREN);
int daysRented = 3;
Rental rental = new Rental(childrenMovie, daysRented);
john.addRental(rental); Sting statement = john.generateTextStatement();
assertEquals(expectedStatement, statement);
}

Zatrzymaj się, czy właśnie skopiowaliśmy dane wyjściowe wygenerowane przez kod i umieszczone w naszym teście. Tak, to jest dokładnie to, co zrobiliśmy.

Nie próbujemy teraz znaleźć błędów. Próbujemy wprowadzić mechanizm, który pozwoli nam znaleźć błędy później, błędy, które ujawnią się jako różnice w stosunku do aktualnego zachowania systemu. Kiedy przyjmiemy tę perspektywę, nasze spojrzenie na testy jest inne: Nie mają one żadnego moralnego autorytetu; po prostu siedzą tam i dokumentują, co system naprawdę robi. Na tym etapie bardzo ważne jest, aby mieć gdzieś wiedzę o tym, co system naprawdę robi.

Pytanie> Jaka jest całkowita liczba testów, które piszemy, aby scharakteryzować system?

Odpowiedź> Jest nieskończona. Moglibyśmy poświęcić sporą część naszego życia na pisanie case’ów dla każdej klasy w starszym kodzie.

Pytanie> Kiedy w takim razie przestajemy? Czy jest jakiś sposób, aby wiedzieć, które przypadki są ważniejsze od innych?

Odpowiedź> Spójrz na kod, który charakteryzujemy. Sam kod może dać nam pomysły na to, co robi, a jeśli mamy pytania, testy są idealnym sposobem na ich zadawanie. W tym momencie piszemy test lub testy, które pokrywają wystarczająco dużą część kodu.

Pytanie> Czy to pokrywa wszystko w kodzie?

Odpowiedź> Może nie. Ale wtedy wykonujemy następny krok. Zastanawiamy się nad zmianami, które chcemy wprowadzić w kodzie i próbujemy ustalić, czy testy, które mamy, wyczują jakiekolwiek problemy, które możemy spowodować. Jeśli nie, dodajemy więcej testów, aż będziemy pewni, że tak się stanie.

How Much To Refactor?

W starszym kodzie jest tak wiele do refaktoryzacji i nie możemy refaktoryzować wszystkiego. Aby odpowiedzieć na to pytanie musimy wrócić do zrozumienia naszego celu refaktoryzacji odziedziczonego kodu.

Chcemy refaktoryzować odziedziczony kod, aby pozostawić go czystszym niż był, gdy do nas przyszedł i aby był zrozumiały dla innych.

Pamiętając o tym, chcemy uczynić system lepszym utrzymując skupienie na zadaniu. Nie chcemy szaleć z refaktoryzacją, próbując przepisać cały system w kilka dni. To co chcemy zrobić to „refaktoryzować kod, który staje nam na drodze do wdrożenia każdej nowej zmiany”. Postaramy się to lepiej zrozumieć na przykładzie w następnym artykule.

Zakończenie

.

Articles

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.