sobota, kwietnia 17, 2010

Eyjafjallajökull w kodowaniu, czyli rozplątujemy kilometry sznurka ;-)

Eyjafjallajökull, czyli nazwa najsłynniejszego i najbardziej kłopotliwego teraz wulkanu w Europie kojarzy mi się nieodparcie z kodowaniem i refactoringiem ;-)
Gdy dostajesz zamotany kod, w którym klasy mają po 3 albo i 5 tysięcy linii, a metody w nich rzadko schodzą poniżej 500 linii to zaczyna się szał refactoringu, który służy głównie zrozumieniu kodu.
Bądźmy szczerzy, nic tak nie pozwala zrozumieć kodu jak jego refactoring. Bo mało kto z nas jest w stanie zrozumieć metodę mającą te 300 linii przy jednym przebiegu, szczególnie gdy złośliwie jest w niej więcej niż 1 ścieżka wykonywania.... a zwykle jest ;-) bo jak chyba wszyscy wiedzą metody mające 300 linii powstają zwykle przez dopisywanie do nich kolejnych ścieżek wykonania.
Zaczynasz od ładnej i krótkiej metody, która ma obliczyć procent składany dla parametrów jakimi są kwota, oprocentowanie roczne, ilość lat. Później przychodzi konieczność uwzględnienia podatki Belki, następnie możliwość naliczania odsetek co miesiąc, co tydzień, co dzień. W końcu okazuje się, że liczyć też trzeba przypadki bez podatku Belki. Jeszcze później konieczność przeliczania między walutami w momencie naliczania odsetek i podatku.... i tak dalej, i nagle z prostej metody BigDecimal calculateGain4Year(BigDecimal cash, BigDecimal yearPercentage, int years), która liczyła powiedzmy 20 linii robi się kobyła mająca tych linii 500. Nazwa w ogóle nie pasuje bo teraz metoda ta wygląda tak:BigDecimal calculateGain4Year(BigDecimal cash, BigDecimal yearPercentage, TimeUnit timeUnit, boolean taxation, Currency cashCurrency, Currency transactionCurrency).
Teraz zaczyna się analiza i używanie Extract Method, po tym refactoringu mamy z jednej multum metod w stylu:
  • BigDecimal calculateGain4DayWithTaxation(BigDecimal cash, BigDecimal yearPercentage, Currency cashCurrency, Currency transactionCurrency),
  • BigDecimal calculateGain4DayWithoutTaxation(BigDecimal cash, BigDecimal yearPercentage, Currency cashCurrency, Currency transactionCurrency),
  • BigDecimal calculateGain4MonthWithTaxation(BigDecimal cash, BigDecimal yearPercentage, Currency cashCurrency, Currency transactionCurrency),
i tak dalej i tak dalej ;-)
Jeżeli jeszcze się okaże, że np. dla kwot w Euro podatek liczy się jeszcze inaczej to będziemy mieli np. metodę: BigDecimal calculateEuroGain4DayWithTaxation(BigDecimal cash, BigDecimal yearPercentage, Currency cashCurrency, Currency transactionCurrency), która będzie używała metody BigDecimal calculateGain4DayWithTaxation(BigDecimal cash, BigDecimal yearPercentage, Currency cashCurrency, Currency transactionCurrency, TaxationEngine engine).
Po tym refactoringu, czyli de facto analizie kodu, czyli po rozłożeniu go na czynniki pierwsze jesteśmy często właśnie na etapie Eyjafjallajökullu ;-) Kod jest bardziej czytalny [czytalny, nie czytelny, przyjmijmy, że ten neologizm oznacza tyle co możliwy do przeczytania], dzięki czemu można go zrozumieć.
Często na tym etapie kończymy.
Nasz zamotany kod/sznurek został rozwinięty, co prawda jest go nadal kilka kilometrów, ale leży na ziemi, widzimy początek i koniec i jest nadzieja, że ciągnąc za jeden koniec dojdziemy do dowolnego jego elementu [tu utrudnienie polegające na tym, że ponieważ to jest jednak kod, a nie sznurek to tak naprawdę ten sznurek ma na sobie pętle, kilka początków i kilka końców i jeszcze trochę dziwnych urozmaiceń ;-)]
Tu przychodzi czas na syntezę zdobytej wiedzy.
Nie zawsze to robimy, ale jak jest nam dane dotrwać do takiego etapu analizy, że zaczynamy rozumieć "co autor miał na myśli" należy jak najszybciej znajdować jakieś uogólnienia.
Tu chyba dobrze użyć tabelki:
w której widzimy wszystkie możliwe przypadki [nie wszystkie tak naprawdę, bo co do okresu naliczania odsetek mamy tylko dni i miesiące, a mogą być jeszcze np. lata czy tygodnie]. To pozwala zaś dostrzec pewne prawidłowości.
Np. to, że sposób naliczania podatku może zależeć od waluty, ale nie zależy np. od czasu tego co ile naliczamy odsetki.
Ba może się okazać, że istnieje kilka przypadków których w ogóle nie powinniśmy oprogramowywać bo są nieokreślone. Np. może się okazać, że dla przypadku z Euro jako walutą nie powinniśmy w ogóle liczyć podatku. Wtedy wiemy, że w takim przypadku powinniśmy np. wyrzucać wyjątek w stylu IllegalArgumentException.
Inna sprawa, że prawie nigdy nie robi się tego w taki sposób, tylko dokonuje się tej syntezy w głowie by po jakimś czasie wracając do kodu zastanawiać się co właściwie mieliśmy na myśli wyrzucając np. obliczanie podatku jako metodę utliową do waluty ;-)
A cały ten przydługi tekst ze względu na śliczną nazwę wulkanu - Eyjafjallajökull :-)


Podobne postybeta
Jest coraz gorzej i będzie jeszcze gorzej 
Cukierki, radosny chaos i takie tam, czyli Przemkowe rojenia ;-)
To i ja planowałem zamach stanu?
Dziura
Dziwne finanse, dziwna gospodarka......