You are on page 1of 30

Czysty kod.

Podrcznik
dobrego programisty
Autor: Robert C. Martin
Tumaczenie: Pawe Gonera
ISBN: 978-83-246-2188-0
Tytu oryginau: Clean Code: A Handbook
of Agile Software Craftsmanship
Format: 168237, stron: 424

Poznaj najlepsze metody tworzenia doskonaego kodu


Jak pisa dobry kod, a zy przeksztaci w dobry?
Jak formatowa kod, aby osign maksymaln czytelno?
Jak implementowa pen obsug bdw bez zamiecania logiki kodu?
O tym, ile problemw sprawia niedbale napisany kod, wie kady programista. Nie
wszyscy jednak wiedz, jak napisa ten wietny, "czysty" kod i czym waciwie powinien
si on charakteryzowa. Co wicej - jak odrni dobry kod od zego? Odpowied na te
pytania oraz sposoby tworzenia czystego, czytelnego kodu znajdziesz wanie w tej
ksice. Podrcznik jest obowizkow pozycj dla kadego, kto chce pozna techniki
rzetelnego i efektywnego programowania.
W ksice Czysty kod. Podrcznik dobrego programisty szczegowo omwione
zostay zasady, wzorce i najlepsze praktyki pisania czystego kodu. Podrcznik zawiera
take kilka analiz przypadkw o coraz wikszej zoonoci, z ktrych kada jest
doskonaym wiczeniem porzdkowania zanieczyszczonego bd nieudanego kodu.
Z tego podrcznika dowiesz si m.in., jak tworzy dobre nazwy, obiekty i funkcje,
a take jak tworzy testy jednostkowe i korzysta z programowania sterowanego
testami. Nauczysz si przeksztaca kod zawierajcy problemy w taki, ktry jest
solidny i efektywny.
Nazwy klas i metod
Funkcje i listy argumentw
Rozdzielanie polece i zapyta
Stosowanie wyjtkw
Komentarze
Formatowanie
Obiekty i struktury danych
Obsuga bdw
Testy jednostkowe
Klasy i systemy
Wspbieno
Oczyszczanie kodu
Niech stworzony przez Ciebie kod imponuje czystoci!

SPIS TRECI

Sowo wstpne

13

Wstp

19

1. Czysty kod

23

Niech stanie si kod...


W poszukiwaniu doskonaego kodu...
Cakowity koszt baaganu
Rozpoczcie wielkiej zmiany projektu
Postawa
Najwiksza zagadka
Sztuka czystego kodu?
Co to jest czysty kod?
Szkoy mylenia
Jestemy autorami
Zasada skautw
Poprzednik i zasady
Zakoczenie
Bibliografia

2. Znaczce nazwy
Wstp
Uywaj nazw przedstawiajcych intencje
Unikanie dezinformacji
Tworzenie wyranych rnic
Tworzenie nazw, ktre mona wymwi
Korzystanie z nazw atwych do wyszukania
Unikanie kodowania
Notacja wgierska
Przedrostki skadnikw
Interfejsy i implementacje
Unikanie odwzorowania mentalnego
Nazwy klas
Nazwy metod
Nie bd dowcipny
Wybieraj jedno sowo na pojcie
Nie twrz kalamburw!
Korzystanie z nazw dziedziny rozwizania
Korzystanie z nazw dziedziny problemu
Dodanie znaczcego kontekstu
Nie naley dodawa nadmiarowego kontekstu
Sowo kocowe

24
24
25
26
27
28
28
28
34
35
36
36
36
37

39
39
40
41
42
43
44
45
45
46
46
47
47
47
48
48
49
49
49
50
51
52
5

3. Funkcje
Mae funkcje!
Bloki i wcicia
Wykonuj jedn czynno
Sekcje wewntrz funkcji
Jeden poziom abstrakcji w funkcji
Czytanie kodu od gry do dou zasada zstpujca
Instrukcje switch
Korzystanie z nazw opisowych
Argumenty funkcji
Czsto stosowane funkcje jednoargumentowe
Argumenty znacznikowe
Funkcje dwuargumentowe
Funkcje trzyargumentowe
Argumenty obiektowe
Listy argumentw
Czasowniki i sowa kluczowe
Unikanie efektw ubocznych
Argumenty wyjciowe
Rozdzielanie polece i zapyta
Stosowanie wyjtkw zamiast zwracania kodw bdw
Wyodrbnienie blokw try-catch
Obsuga bdw jest jedn operacj
Przyciganie zalenoci w Error.java
Nie powtarzaj si
Programowanie strukturalne
Jak pisa takie funkcje?
Zakoczenie
SetupTeardownIncluder
Bibliografia

4. Komentarze
Komentarze nie s szmink dla zego kodu
Czytelny kod nie wymaga komentarzy
Dobre komentarze
Komentarze prawne
Komentarze informacyjne
Wyjanianie zamierze
Wyjanianie
Ostrzeenia o konsekwencjach
Komentarze TODO
Wzmocnienie
Komentarze Javadoc w publicznym API
Ze komentarze
Bekot
Powtarzajce si komentarze
Mylce komentarze
Komentarze wymagane
Komentarze dziennika

SPIS TRECI

53
56
57
57
58
58
58
59
61
62
62
63
63
64
64
65
65
65
66
67
67
68
69
69
69
70
70
71
71
73

75
77
77
77
77
78
78
79
80
80
81
81
81
81
82
84
85
85

Komentarze wprowadzajce szum informacyjny


Przeraajcy szum
Nie uywaj komentarzy, jeeli mona uy funkcji lub zmiennej
Znaczniki pozycji
Komentarze w klamrach zamykajcych
Atrybuty i dopiski
Zakomentowany kod
Komentarze HTML
Informacje nielokalne
Nadmiar informacji
Nieoczywiste poczenia
Nagwki funkcji
Komentarze Javadoc w niepublicznym kodzie
Przykad
Bibliografia

86
87
88
88
88
89
89
90
91
91
91
92
92
92
95

5. Formatowanie

97

Przeznaczenie formatowania
Formatowanie pionowe
Metafora gazety
Pionowe odstpy pomidzy segmentami kodu
Gsto pionowa
Odlego pionowa
Uporzdkowanie pionowe
Formatowanie poziome
Poziome odstpy i gsto
Rozmieszczenie poziome
Wcicia
Puste zakresy
Zasady zespoowe
Zasady formatowania wujka Boba

98
98
99
99
101
101
105
106
106
107
109
110
110
111

6. Obiekty i struktury danych

113

Abstrakcja danych
Antysymetria danych i obiektw
Prawo Demeter
Wraki pocigw
Hybrydy
Ukrywanie struktury
Obiekty transferu danych
Active Record
Zakoczenie
Bibliografia

113
115
117
118
118
119
119
120
121
121

7. Obsuga bdw

123

Uycie wyjtkw zamiast kodw powrotu


Rozpoczynanie od pisania instrukcji try-catch-finally
Uycie niekontrolowanych wyjtkw
Dostarczanie kontekstu za pomoc wyjtkw
Definiowanie klas wyjtkw w zalenoci od potrzeb wywoujcego

124
125
126
127
127

SPIS TRECI

Definiowanie normalnego przepywu


Nie zwracamy null
Nie przekazujemy null
Zakoczenie
Bibliografia

8. Granice
Zastosowanie kodu innych firm
Przegldanie i zapoznawanie si z granicami
Korzystanie z pakietu log4j
Zalety testw uczcych
Korzystanie z nieistniejcego kodu
Czyste granice
Bibliografia

9. Testy jednostkowe
Trzy prawa TDD
Zachowanie czystoci testw
Testy zwikszaj moliwoci
Czyste testy
Jzyki testowania specyficzne dla domeny
Podwjny standard
Jedna asercja na test
Jedna koncepcja na test
F.I.R.S.T.
Zakoczenie
Bibliografia

10. Klasy
Organizacja klas
Hermetyzacja
Klasy powinny by mae!
Zasada pojedynczej odpowiedzialnoci
Spjno
Utrzymywanie spjnoci powoduje powstanie wielu maych klas
Organizowanie zmian
Izolowanie moduw kodu przed zmianami
Bibliografia

11. Systemy
Jak budowaby miasto?
Oddzielenie konstruowania systemu od jego uywania
Wydzielenie moduu main
Fabryki
Wstrzykiwanie zalenoci
Skalowanie w gr
Separowanie (rozcicie) problemw
Poredniki Java

SPIS TRECI

129
130
131
132
132

133
134
136
136
138
138
139
140

141
142
143
144
144
147
147
149
150
151
152
152

153
153
154
154
156
158
158
164
166
167

169
170
170
171
172
172
173
176
177

Czyste biblioteki Java AOP


Aspekty w AspectJ
Testowanie architektury systemu
Optymalizacja podejmowania decyzji
Korzystaj ze standardw, gdy wnosz realn warto
Systemy wymagaj jzykw dziedzinowych
Zakoczenie
Bibliografia

178
181
182
183
183
184
184
185

12. Powstawanie projektu

187

Uzyskiwanie czystoci projektu przez jego rozwijanie


Zasada numer 1 prostego projektu system przechodzi wszystkie testy
Zasady numer 2 4 prostego projektu przebudowa
Brak powtrze
Wyrazisto kodu
Minimalne klasy i metody
Zakoczenie
Bibliografia

187
188
188
189
191
192
192
192

13. Wspbieno

193

W jakim celu stosowa wspbieno?


Mity i nieporozumienia
Wyzwania
Zasady obrony wspbienoci
Zasada pojedynczej odpowiedzialnoci
Wniosek ograniczenie zakresu danych
Wniosek korzystanie z kopii danych
Wniosek wtki powinny by na tyle niezalene, na ile to tylko moliwe
Poznaj uywan bibliotek
Kolekcje bezpieczne dla wtkw
Poznaj modele wykonania
Producent-konsument
Czytelnik-pisarz
Ucztujcy filozofowie
Uwaga na zalenoci pomidzy synchronizowanymi metodami
Tworzenie maych sekcji synchronizowanych
Pisanie prawidowego kodu wyczajcego jest trudne
Testowanie kodu wtkw
Traktujemy przypadkowe awarie jako potencjalne problemy z wielowtkowoci
Na pocztku uruchamiamy kod niekorzystajcy z wtkw
Nasz kod wtkw powinien da si wcza
Nasz kod wtkw powinien da si dostraja
Uruchamiamy wicej wtkw, ni mamy do dyspozycji procesorw
Uruchamiamy testy na rnych platformach
Uzbrajamy nasz kod w elementy prbujce wywoa awarie i wymuszajce awarie
Instrumentacja rczna
Instrumentacja automatyczna
Zakoczenie
Bibliografia

194
195
196
196
197
197
197
198
198
198
199
199
200
200
201
201
202
202
203
203
203
204
204
204
205
205
206
207
208

SPIS TRECI

14. Udane oczyszczanie kodu

209

Implementacja klasy Args


Args zgrubny szkic
Argumenty typu String
Zakoczenie

210
216
228
261

15. Struktura biblioteki JUnit


Biblioteka JUnit
Zakoczenie

16. Przebudowa klasy SerialDate

263
264
276

277

Na pocztek uruchamiamy
Teraz poprawiamy
Zakoczenie
Bibliografia

278
280
293
294

17. Zapachy kodu i heurystyki

295

Komentarze
C1. Niewaciwe informacje
C2. Przestarzae komentarze
C3. Nadmiarowe komentarze
C4. le napisane komentarze
C5. Zakomentowany kod
rodowisko
E1. Budowanie wymaga wicej ni jednego kroku
E2. Testy wymagaj wicej ni jednego kroku
Funkcje
F1. Nadmiar argumentw
F2. Argumenty wyjciowe
F3. Argumenty znacznikowe
F4. Martwe funkcje
Oglne
G1. Wiele jzykw w jednym pliku rdowym
G2. Oczywiste dziaanie jest nieimplementowane
G3. Niewaciwe dziaanie w warunkach granicznych
G4. Zdjte zabezpieczenia
G5. Powtrzenia
G6. Kod na nieodpowiednim poziomie abstrakcji
G7. Klasy bazowe zalene od swoich klas pochodnych
G8. Za duo informacji
G9. Martwy kod
G10. Separacja pionowa
G11. Niespjno
G12. Zaciemnianie
G13. Sztuczne sprzenia
G14. Zazdro o funkcje
G15. Argumenty wybierajce
G16. Zaciemnianie intencji
G17. le rozmieszczona odpowiedzialno

10

SPIS TRECI

296
296
296
296
297
297
297
297
297
298
298
298
298
298
298
298
299
299
299
300
300
301
302
302
303
303
303
303
304
305
305
306

G18. Niewaciwe metody statyczne


G19. Uycie opisowych zmiennych
G20. Nazwy funkcji powinny informowa o tym, co realizuj
G21. Zrozumienie algorytmu
G22. Zamiana zalenoci logicznych na fizyczne
G23. Zastosowanie polimorfizmu zamiast instrukcji if-else lub switch-case
G24. Wykorzystanie standardowych konwencji
G25. Zamiana magicznych liczb na stae nazwane
G26. Precyzja
G27. Struktura przed konwencj
G28. Hermetyzacja warunkw
G29. Unikanie warunkw negatywnych
G30. Funkcje powinny wykonywa jedn operacj
G31. Ukryte sprzenia czasowe
G32. Unikanie dowolnych dziaa
G33. Hermetyzacja warunkw granicznych
G34. Funkcje powinny zagbia si na jeden poziom abstrakcji
G35. Przechowywanie danych konfigurowalnych na wysokim poziomie
G36. Unikanie nawigacji przechodnich

306
307
307
308
308
309
310
310
311
312
312
312
312
313
314
314
315
316
317
317
317
318
319
320
320
321
322
322
323
323
323
324
324
324
324
324
324
324
324
325
325
325
325

Java
J1. Unikanie dugich list importu przez uycie znakw wieloznacznych
J2. Nie dziedziczymy staych
J3. Stae kontra typy wyliczeniowe
Nazwy
N1. Wybr opisowych nazw
N2. Wybr nazw na odpowiednich poziomach abstrakcji
N3. Korzystanie ze standardowej nomenklatury tam, gdzie jest to moliwe
N4. Jednoznaczne nazwy
N5. Uycie dugich nazw dla dugich zakresw
N6. Unikanie kodowania
N7. Nazwy powinny opisywa efekty uboczne
Testy
T1. Niewystarczajce testy
T2. Uycie narzdzi kontroli pokrycia
T3. Nie pomijaj prostych testw
T4. Ignorowany test jest wskazaniem niejednoznacznoci
T5. Warunki graniczne
T6. Dokadne testowanie pobliskich bdw
T7. Wzorce bdw wiele ujawniaj
T8. Wzorce pokrycia testami wiele ujawniaj
T9. Testy powinny by szybkie
Zakoczenie
Bibliografia

A Wspbieno II

327

Przykad klient-serwer
Serwer
Dodajemy wtki
Uwagi na temat serwera
Zakoczenie

327
327
329
329
331

SPIS TRECI

11

Moliwe cieki wykonania


Liczba cieek
Kopiemy gbiej
Zakoczenie
Poznaj uywan bibliotek
Biblioteka Executor
Rozwizania nieblokujce
Bezpieczne klasy nieobsugujce wtkw
Zalenoci midzy metodami mog uszkodzi kod wspbieny
Tolerowanie awarii
Blokowanie na kliencie
Blokowanie na serwerze
Zwikszanie przepustowoci
Obliczenie przepustowoci jednowtkowej
Obliczenie przepustowoci wielowtkowej
Zakleszczenie
Wzajemne wykluczanie
Blokowanie i oczekiwanie
Brak wywaszczania
Cykliczne oczekiwanie
Zapobieganie wzajemnemu wykluczaniu
Zapobieganie blokowaniu i oczekiwaniu
Umoliwienie wywaszczania
Zapobieganie oczekiwaniu cyklicznemu
Testowanie kodu wielowtkowego
Narzdzia wspierajce testowanie kodu korzystajcego z wtkw
Zakoczenie
Samouczek. Peny kod przykadw
Klient-serwer bez wtkw
Klient-serwer z uyciem wtkw

12

331
332
333
336
336
336
337
338
339
340
340
342
343
344
344
345
346
346
346
346
347
347
348
348
349
351
352
352
352
355

B org.jfree.date.SerialDate

357

C Odwoania do heurystyk

411

Epilog

413

Skorowidz

415

SPIS TRECI

ROZDZIA 4.

Komentarze

Nie komentuj zego kodu popraw go.


Brian W. Kernighan i P.J. Plaugher1

, jak dobrze umieszczony komentarz. Jednoczenie nic tak nie


N zaciemnia moduu, jak kilka zbyt dogmatycznych
komentarzy. Nic nie jest tak szkodliwe, jak stary
IEWIELE JEST RZECZY TAK POMOCNYCH

komentarz szerzcy kamstwa i dezinformacj.


Komentarze nie s jak Lista Schindlera. Nie s one czystym dobrem. W rzeczywistoci komentarze s w najlepszym przypadku koniecznym zem. Jeeli nasz jzyk programowania jest wystarczajco ekspresyjny lub mamy wystarczajcy talent, by wykorzystywa ten jzyk, aby wyraa
nasze zamierzenia, nie bdziemy potrzebowa zbyt wielu komentarzy.

[KP78], s. 144.

75

Prawidowe zastosowanie komentarzy jest kompensowaniem naszych bdw przy tworzeniu kodu.
Prosz zwrci uwag, e uyem sowa bd. Dokadnie to miaem na myli. Obecno komentarzy
zawsze sygnalizuje nieporadno programisty. Musimy korzysta z nich, poniewa nie zawsze wiemy,
jak wyrazi nasze intencje bez ich uycia, ale ich obecno nie jest powodem do witowania.
Gdy uznamy, e konieczne jest napisanie komentarza, naley pomyle, czy nie istnieje sposb na
wyraenie tego samego w kodzie. Za kadym razem, gdy wyrazimy to samo za pomoc kodu, powinnimy odczuwa satysfakcj. Za kadym razem, gdy piszemy komentarz, powinnimy poczu
smak poraki.
Dlaczego jestem tak przeciwny komentarzom? Poniewa one kami. Nie zawsze, nie rozmylnie,
ale nader czsto. Im starsze s komentarze, tym wiksze prawdopodobiestwo, e s po prostu
bdne. Powd jest prosty. Programici nie s w stanie utrzymywa ich aktualnoci.
Kod zmienia si i ewoluuje. Jego fragmenty s przenoszone w rne miejsca. Fragmenty te s rozdzielane, odtwarzane i ponownie czone. Niestety, komentarze nie zawsze za nimi podaj nie
zawsze mog by przenoszone. Zbyt czsto komentarze s odczane od kodu, ktry opisuj, i staj
si osieroconymi notatkami o stale zmniejszajcej si dokadnoci. Dla przykadu warto spojrze,
co si stao z komentarzem i wierszem, ktrego dotyczy:
MockRequest request;
private final String HTTP_DATE_REGEXP =
"[SMTWF][a-z]{2}\\,\\s[0-9]{2}\\s[JFMASOND][a-z]{2}\\s"+
"[0-9]{4}\\s[0-9]{2}\\:[0-9]{2}\\:[0-9]{2}\\sGMT";
private Response response;
private FitNesseContext context;
private FileResponder responder;
private Locale saveLocale;

// Przykad: "Tue, 02 Apr 2003 22:18:49 GMT"

Pozostae zmienne instancyjne zostay prawdopodobnie pniej dodane pomidzy sta HTTP_
DATE_REGEXP a objaniajcym j komentarzem.
Mona oczywicie stwierdzi, e programici powinni by na tyle zdyscyplinowani, aby utrzymywa komentarze w naleytym stanie. Zgadzam si, powinni. Wolabym jednak, aby powicona na
to energia zostaa spoytkowana na zapewnienie takiej precyzji i wyrazistoci kodu, by komentarze
okazay si zbdne.
Niedokadne komentarze s znacznie gorsze ni ich brak. Kami i wprowadzaj w bd. Powoduj
powstanie oczekiwa, ktre nigdy nie s spenione. Definiuj stare zasady, ktre nie s ju potrzebne lub nie powinny by stosowane.
Prawda znajduje si w jednym miejscu: w kodzie. Jedynie kod moe niezawodnie przedstawi to,
co realizuje. Jest jedynym rdem naprawd dokadnych informacji. Dlatego cho komentarze s
czasami niezbdne, powicimy spor ilo energii na zminimalizowanie ich liczby.

76

ROZDZIA 4.

Komentarze nie s szmink dla zego kodu


Jednym z czsto spotykanych powodw pisania komentarzy jest nieudany kod. Napisalimy modu
i zauwaamy, e jest le zorganizowany. Wiemy, e jest chaotyczny. Mwimy wwczas: Hm, bdzie
lepiej, jak go skomentuj. Nie! Lepiej go poprawi!
Precyzyjny i czytelny kod z ma liczb komentarzy jest o wiele lepszy ni zabaaganiony i zoony
kod z mnstwem komentarzy. Zamiast spdza czas na pisaniu kodu wyjaniajcego baagan, jaki
zrobilimy, warto powici czas na posprztanie tego baaganu.

Czytelny kod nie wymaga komentarzy


W wielu przypadkach kod mgby zupenie obej si bez komentarzy, a jednak programici wol
umieci w nim komentarz, zamiast zawrze objanienia w samym kodzie. Spjrzmy na poniszy
przykad. Co wolelibymy zobaczy? To:
// Sprawdzenie, czy pracownik ma prawo do wszystkich korzyci
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))

czy to:
if (employee.isEligibleForFullBenefits())

Przeznaczenie tego kodu jest jasne po kilku sekundach mylenia. W wielu przypadkach jest to wycznie kwestia utworzenia funkcji, ktra wyraa to samo co komentarz, jaki chcemy napisa.

Dobre komentarze
Czasami komentarze s niezbdne lub bardzo przydatne. Przedstawimy kilka przypadkw, w ktrych
uznalimy, e warto powici im czas. Naley jednak pamita, e naprawd dobry komentarz to
taki, dla ktrego znalelimy powd, aby go nie pisa.

Komentarze prawne
Korporacyjne standardy kodowania czasami wymuszaj na nas pisanie pewnych komentarzy
z powodw prawnych. Na przykad informacje o prawach autorskich s niezbdnym elementem
umieszczanym w komentarzu na pocztku kadego pliku rdowego.
Przykadem moe by standardowy komentarz, jaki umieszczalimy na pocztku kadego pliku
rdowego w FitNesse. Na szczcie nasze rodowisko IDE ukrywa te komentarze przez ich automatyczne zwinicie.
// Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the GNU General Public License version 2 or later.

KOMENTARZE

77

Tego typu komentarze nie powinny by wielkoci umw lub kodeksw. Tam, gdzie to moliwe,
warto odwoywa si do standardowych licencji lub zewntrznych dokumentw, a nie umieszcza
w komentarzu wszystkich zasad i warunkw.

Komentarze informacyjne
Czasami przydatne jest umieszczenie w komentarzu podstawowych informacji. Na przykad w poniszym komentarzu objaniamy warto zwracan przez metod abstrakcyjn.
// Zwraca testowany obiekt Responder.
protected abstract Responder responderInstance();

Komentarze tego typu s czasami przydatne, ale tam, gdzie to moliwe, lepiej jest skorzysta
z nazwy funkcji do przekazania informacji. Na przykad w tym przypadku komentarz moe sta si
niepotrzebny, jeeli zmienimy nazw funkcji: responderBeingTested.
Poniej mamy nieco lepszy przypadek:
// Dopasowywany format kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile(
"\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");

W tym przypadku komentarze pozwalaj nam poinformowa, e uyte wyraenie regularne ma


dopasowa czas i dat sformatowane za pomoc funkcji SimpleDateFormat.format z uyciem
zdefiniowanego formatu. Nadal lepiej jest przenie kod do specjalnej klasy pozwalajcej na konwertowanie formatw daty i czasu. Po tej operacji komentarz najprawdopodobniej stanie si zbdny.

Wyjanianie zamierze
W niektrych przypadkach komentarze zawieraj informacje nie tylko o implementacji, ale take
o powodach podjcia danej decyzji. W poniszym przypadku widzimy interesujc decyzj udokumentowan w postaci komentarza. Przy porwnywaniu obiektw autor zdecydowa o tym, e
obiekty jego klasy bd po posortowaniu wyej ni obiekty pozostaych klas.
public int compareTo(Object o)
{
if(o instanceof WikiPagePath)
{
WikiPagePath p = (WikiPagePath) o;
String compressedName = StringUtil.join(names, "");
String compressedArgumentName = StringUtil.join(p.names, "");
return compressedName.compareTo(compressedArgumentName);
}
return 1; // Jestemy wiksi, poniewa jestemy waciwego typu.
}

Poniej pokazany jest lepszy przykad. Moemy nie zgadza si z rozwizaniem tego problemu
przez programist, ale przynajmniej wiemy, co prbowa zrobi.
public void testConcurrentAddWidgets() throws Exception {
WidgetBuilder widgetBuilder =
new WidgetBuilder(new Class[]{BoldWidget.class});
String text = "'''bold text'''";

78

ROZDZIA 4.

ParentWidget parent =
new BoldWidget(new MockWidgetRoot(), "'''bold text'''");
AtomicBoolean failFlag = new AtomicBoolean();
failFlag.set(false);

//Jest to nasza prba uzyskania wycigu


//przez utworzenie duej liczby wtkw.
for (int i = 0; i < 25000; i++) {
WidgetBuilderThread widgetBuilderThread =
new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
Thread thread = new Thread(widgetBuilderThread);
thread.start();
}
assertEquals(false, failFlag.get());
}

Wyjanianie
Czasami przydatne jest wytumaczenie znaczenia niejasnych argumentw lub zwracanych wartoci.
Zwykle lepiej jest znale sposb na to, by ten argument lub zwracana warto byy bardziej czytelne,
ale jeeli s one czci biblioteki standardowej lub kodu, ktrego nie moemy zmienia, to
wyjanienia w komentarzach mog by uyteczne.
public void testCompareTo() throws Exception
{
WikiPagePath a = PathParser.parse("PageA");
WikiPagePath ab = PathParser.parse("PageA.PageB");
WikiPagePath b = PathParser.parse("PageB");
WikiPagePath aa = PathParser.parse("PageA.PageA");
WikiPagePath bb = PathParser.parse("PageB.PageB");
WikiPagePath ba = PathParser.parse("PageB.PageA");
assertTrue(a.compareTo(a) == 0); // a == a
assertTrue(a.compareTo(b) != 0); // a != b
assertTrue(ab.compareTo(ab) == 0); // ab == ab
assertTrue(a.compareTo(b) == -1); // a < b
assertTrue(aa.compareTo(ab) == -1); // aa < ab
assertTrue(ba.compareTo(bb) == -1); // ba < bb
assertTrue(b.compareTo(a) == 1); // b > a
assertTrue(ab.compareTo(aa) == 1); // ab > aa
assertTrue(bb.compareTo(ba) == 1); // bb > ba
}

Istnieje oczywicie spore ryzyko, e komentarze objaniajce s nieprawidowe. Warto przeanalizowa poprzedni przykad i zobaczy, jak trudno jest sprawdzi, czy s one prawidowe. Wyjania to,
dlaczego niezbdne s objanienia i dlaczego s one ryzykowne. Tak wic przed napisaniem tego
typu komentarzy naley sprawdzi, czy nie istnieje lepszy sposb, a nastpnie powici im wicej
uwagi, aby byy precyzyjne.

KOMENTARZE

79

Ostrzeenia o konsekwencjach
Komentarze mog rwnie suy do ostrzegania innych
programistw o okrelonych konsekwencjach. Poniszy
komentarz wyjania, dlaczego przypadek testowy jest
wyczony:
// Nie uruchamiaj, chyba e masz nieco czasu do zagospodarowania.
public void _testWithReallyBigFile()
{
writeLinesToFile(10000000);
response.setBody(testFile);
response.readyToSend(this);
String responseString = output.toString();
assertSubString("Content-Length:
1000000000", responseString);
assertTrue(bytesSent > 1000000000);
}

Obecnie oczywicie wyczamy przypadek testowy przez uycie atrybutu @Ignore z odpowiednim
tekstem wyjaniajcym. @Ignore("Zajmuje zbyt duo czasu"). Jednak w czasach przed JUnit 4
umieszczenie podkrelenia przed nazw metody byo czsto stosowan konwencj. Komentarz,
cho nonszalancki, dosy dobrze wskazuje powd.
Poniej pokazany jest inny przykad:
public static SimpleDateFormat makeStandardHttpDateFormat()
{

//SimpleDateFormat nie jest bezpieczna dla wtkw,


//wic musimy kady obiekt tworzy niezalenie.
SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("GMT"));
return df;
}

Mona narzeka, e istniej lepsze sposoby rozwizania tego problemu. Mog si z tym zgodzi.
Jednak zastosowany tu komentarz jest cakiem rozsdny. Moe on powstrzyma nadgorliwego
programist przed uyciem statycznego inicjalizera dla zapewnienia lepszej wydajnoci.

Komentarze TODO
Czasami dobrym pomysem jest pozostawianie notatek do zrobienia w postaci komentarzy
//TODO. W zamieszczonym poniej przypadku komentarz TODO wyjania, dlaczego funkcja ma
zdegenerowan implementacj i jaka powinna by jej przyszo.
//TODO-MdM Nie jest potrzebna.
// Oczekujemy, e zostanie usunita po pobraniu modelu.
protected VersionInfo makeVersion() throws Exception
{
return null;
}

80

ROZDZIA 4.

Komentarze TODO oznaczaj zadania, ktre wedug programisty powinny by wykonane, ale
z pewnego powodu nie mona tego zrobi od razu. Moe to by przypomnienie o koniecznoci
usunicia przestarzaej funkcji lub proba do innej osoby o zajcie si problemem. Moe to by danie, aby kto pomyla o nadaniu lepszej nazwy, lub przypomnienie o koniecznoci wprowadzenia zmiany zalenej od planowanego zdarzenia. Niezalenie od tego, czym jest TODO, nie moe to by
wymwka dla pozostawienia zego kodu w systemie.
Obecnie wiele dobrych IDE zapewnia specjalne funkcje lokalizujce wszystkie komentarze TODO, wic
jest mao prawdopodobne, aby zostay zgubione. Nadal jednak nie jest korzystne, by kod by nafaszerowany komentarzami TODO. Naley wic regularnie je przeglda i eliminowa wszystkie, ktre si da.

Wzmocnienie
Komentarz moe by uyty do wzmocnienia wagi operacji, ktra w przeciwnym razie moe wydawa si niekonsekwencj.
String listItemContent = match.group(3).trim();

// Wywoanie trim jest naprawd wane. Usuwa pocztkowe


// spacje, ktre mog spowodowa, e element bdzie
// rozpoznany jako kolejna lista.
new ListItemWidget(this, listItemContent, this.level + 1);
return buildList(text.substring(match.end()));

Komentarze Javadoc w publicznym API


Nie ma nic bardziej pomocnego i satysfakcjonujcego, jak dobrze opisane publiczne API. Przykadem tego moe by standardowa biblioteka Java. Bez niej pisanie programw Java byoby trudne,
o ile nie niemoliwe.
Jeeli piszemy publiczne API, to niezbdne jest napisanie dla niego dobrej dokumentacji Javadoc.
Jednak naley pamita o pozostaych poradach z tego rozdziau. Komentarze Javadoc mog by
rwnie mylce, nie na miejscu i nieszczere jak wszystkie inne komentarze.

Ze komentarze
Do tej kategorii naley wikszo komentarzy. Zwykle s to podpory zego kodu lub wymwki albo
uzasadnienie niewystarczajcych decyzji znaczce niewiele wicej ni dyskusja programisty ze sob.

Bekot
Pisanie komentarza tylko dlatego, e czujemy, i powinien by napisany lub te e wymaga tego
proces, jest bdem. Jeeli decydujemy si na napisanie komentarza, musimy powici nieco czasu
na upewnienie si, e jest to najlepszy komentarz, jaki moglimy napisa.

KOMENTARZE

81

Poniej zamieszczony jest przykad znaleziony w FitNesse. Komentarz by faktycznie przydatny. Jednak
autor pieszy si lub nie powici mu zbyt wiele uwagi. Bekot, ktry po sobie zostawi, stanowi
nie lada zagadk:
public void loadProperties()
{
try
{
String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE;
FileInputStream propertiesStream = new FileInputStream(propertiesPath);
loadedProperties.load(propertiesStream);
}
catch(IOException e)
{

// Brak plikw waciwoci oznacza zaadowanie wszystkich wartoci domylnych.


}
}

Co oznacza komentarz w bloku catch? Jasne jest, e znaczy on co dla autora, ale znaczenie to nie
zostao dobrze wyartykuowane. Jeeli otrzymamy wyjtek IOException, najwyraniej oznacza to
brak pliku waciwoci, a w takim przypadku adowane s wszystkie wartoci domylne. Jednak kto
aduje te wartoci domylne? Czy byy zaadowane przed wywoaniem loadProperties.load?
Czy te loadProperties.load przechwytuje wyjtek, aduje wartoci domylne i przekazuje nam
wyjtek do zignorowania? A moe loadProperties.load aduje wszystkie wartoci domylne
przed prb zaadowania pliku? Czy autor prbowa usprawiedliwi przed samym sob fakt, e pozostawi pusty blok catch? By moe ta moliwo jest nieco przeraajca autor prbowa
powiedzie sobie, e powinien wrci w to miejsce i napisa kod adujcy wartoci domylne.
Jedynym sposobem, aby si tego dowiedzie, jest przeanalizowanie kodu z innych czci systemu
i sprawdzenie, co si w nich dzieje. Wszystkie komentarze, ktre wymuszaj zagldanie do innych
moduw w celu ich zrozumienia, nie s warte bitw, ktre zajmuj.

Powtarzajce si komentarze
Na listingu 4.1 zamieszczona jest prosta funkcja z komentarzem w nagwku, ktry jest cakowicie
zbdny. Prawdopodobnie duej zajmuje przeczytanie komentarza ni samego kodu.
L I S T I N G 4 . 1 . waitForClose
// Metoda uytkowa koczca prac, gdy this.closed ma warto true. Zgasza wyjtek,
// jeeli przekroczony zostanie czas oczekiwania.
public synchronized void waitForClose(final long timeoutMillis)
throws Exception
{
if(!closed)
{
wait(timeoutMillis);
if(!closed)
throw new Exception("MockResponseSender could not be closed");
}
}

Czemu suy ten komentarz? Przecie nie niesie wicej informacji ni sam kod. Nie uzasadnia on
kodu, nie przedstawia zamierze ani przyczyn. Nie jest atwiejszy do czytania od samego kodu.
82

ROZDZIA 4.

W rzeczywistoci jest mniej precyzyjny ni kod i wymusza na czytelniku zaakceptowanie braku


precyzji w imi prawdziwego zrozumienia. Jest on podobny do paplania sprzedawcy uywanych
samochodw, ktry zapewnia, e nie musisz zaglda pod mask.
Spjrzmy teraz na legion bezuytecznych i nadmiarowych komentarzy Javadoc pobranych z programu Tomcat i zamieszczonych na listingu 4.2. Komentarze te maj za zadanie wycznie zaciemni i popsu kod. Nie maj one adnej wartoci dokumentujcej. Co gorsza, pokazaem tutaj tylko
kilka pierwszych. W tym module znajduje si znacznie wicej takich komentarzy.
L I S T I N G 4 . 2 . ContainerBase.java (Tomcat)
public abstract class ContainerBase
implements Container, Lifecycle, Pipeline,
MBeanRegistration, Serializable {

/**
* The processor delay for this component.
*/
protected int backgroundProcessorDelay = -1;

/**
* The lifecycle event support for this component.
*/
protected LifecycleSupport lifecycle =
new LifecycleSupport(this);

/**
* The container event listeners for this Container.
*/
protected ArrayList listeners = new ArrayList();

/**
* The Loader implementation with which this Container is
* associated.
*/
protected Loader loader = null;

/**
* The Logger implementation with which this Container is
* associated.
*/
protected Log logger = null;

/**
* Associated logger name.
*/
protected String logName = null;

/**
* The Manager implementation with which this Container is
* associated.
*/
protected Manager manager = null;

/**
* The cluster with which this Container is associated.
*/
protected Cluster cluster = null;

KOMENTARZE

83

/**
* The human-readable name of this Container.
*/
protected String name = null;

/**
* The parent Container to which this Container is a child.
*/
protected Container parent = null;

/**
* The parent class loader to be configured when we install a
* Loader.
*/
protected ClassLoader parentClassLoader = null;

/**
* The Pipeline object with which this Container is
* associated.
*/
protected Pipeline pipeline = new StandardPipeline(this);

/**
* The Realm with which this Container is associated.
*/
protected Realm realm = null;

/**
* The resources DirContext object with which this Container
* is associated.
*/
protected DirContext resources = null;

Mylce komentarze
Czasami pomimo najlepszych intencji programista zapisuje w komentarzu nieprecyzyjne zdania.
Wrmy na moment do nadmiarowego, ale rwnie nieco mylcego komentarza zamieszczonego
na listingu 4.1.
Czy Czytelnik zauway, w czym ten komentarz jest mylcy? Metoda ta nie koczy si, gdy
this.closed ma warto true. Koczy si ona, jeeli this.closed ma warto true; w przeciwnym razie czeka okrelony czas, a nastpnie zgasza wyjtek, jeeli this.closed nadal nie ma
wartoci true.
Ta subtelna dezinformacja umieszczona w komentarzu, ktry czyta si trudniej ni sam kod, moe
spowodowa, e inny programista naiwnie wywoa t funkcj, oczekujc, e zakoczy si od razu,
gdy this.closed przyjmie warto true. Ten biedny programista moe zorientowa si, o co
chodzi, dopiero w sesji debugera, gdy bdzie prbowa zorientowa si, dlaczego jego kod dziaa
tak powoli.

84

ROZDZIA 4.

Komentarze wymagane
Wymaganie, aby kada funkcja posiadaa Javadoc lub aby kada zmienna posiadaa komentarz,
jest po prostu gupie. Tego typu komentarze tylko zaciemniaj kod i prowadz do powszechnych
pomyek i dezorganizacji.
Na przykad wymaganie komentarza Javadoc prowadzi do powstania takich potworw, jak ten
zamieszczony na listingu 4.3. Takie komentarze nie wnosz niczego, za to utrudniaj zrozumienie
kodu.
LISTING 4.3.
/**
*
* @param title Tytu pyty CD
* @param author Autor pyty CD
* @param tracks Liczba cieek na pycie CD
* @param durationInMinutes Czas odtwarzania CD w minutach
*/
public void addCD(String title, String author,
int tracks, int durationInMinutes) {
CD cd = new CD();
cd.title = title;
cd.author = author;
cd.tracks = tracks;
cd.duration = duration;
cdList.add(cd);
}

Komentarze dziennika
Czasami programici dodaj na pocztku kadego pliku komentarz informujcy o kadej edycji. Komentarze takie tworz pewnego rodzaju dziennik wszystkich wprowadzonych zmian. Spotkaem si
z moduami zawierajcymi kilkanacie stron z kolejnymi pozycjami dziennika.
* Changes (from 11-Oct-2001)
* -------------------------* 11-Oct-2001 : Re-organised the class and moved it to new package
* com.jrefinery.date (DG);
* 05-Nov-2001 : Added a getDescription() method, and eliminated NotableDate
* class (DG);
* 12-Nov-2001 : IBD requires setDescription() method, now that NotableDate
* class is gone (DG); Changed getPreviousDayOfWeek(),
* getFollowingDayOfWeek() and getNearestDayOfWeek() to correct
* bugs (DG);
* 05-Dec-2001 : Fixed bug in SpreadsheetDate class (DG);
* 29-May-2002 : Moved the month constants into a separate interface
* (MonthConstants) (DG);
* 27-Aug-2002 : Fixed bug in addMonths() method, thanks to N???levka Petr (DG);
* 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
* 13-Mar-2003 : Implemented Serializable (DG);
* 29-May-2003 : Fixed bug in addMonths method (DG);
* 04-Sep-2003 : Implemented Comparable. Updated the isInRange javadocs (DG);
* 05-Jan-2005 : Fixed bug in addYears() method (1096282) (DG);

KOMENTARZE

85

Dawno temu istniay powody tworzenia i utrzymywania takich dziennikw na pocztku kadego
moduu. Nie mielimy po prostu systemw kontroli wersji, ktre wykonyway to za nas. Obecnie
jednak takie dugie dzienniki tylko pogarszaj czytelno moduu. Powinny zosta usunite.

Komentarze wprowadzajce szum informacyjny


Czasami zdarza si nam spotka komentarze, ktre nie s niczym wicej jak tylko szumem informacyjnym. Przedstawiaj one oczywiste dane i nie dostarczaj adnych nowych informacji.
/**
* Konstruktor domylny.
*/
protected AnnualDateRule() {
}

No nie, naprawd? Albo co takiego:


/** Dzie miesica. */
private int dayOfMonth;

Nastpnie mamy doskonay przykad nadmiarowoci:


/**
* Zwraca dzie miesica.
*
* @return dzie miesica.
*/
public int getDayOfMonth() {
return dayOfMonth;
}

Komentarze takie stanowi tak duy szum informacyjny, e nauczylimy si je ignorowa. Gdy
czytamy kod, nasze oczy po prostu je pomijaj. W kocu komentarze te gosz nieprawd, gdy otaczajcy kod jest zmieniany.
Pierwszy komentarz z listingu 4.4 wydaje si waciwy2. Wyjania powd zignorowania bloku catch.
Jednak drugi jest czystym szumem. Najwyraniej programista by tak sfrustrowany pisaniem blokw
try-catch w tej funkcji, e musia sobie uly.
L I S T I N G 4 . 4 . startSending
private void startSending()
{
try
{
doSending();
}
catch(SocketException e)
{

// Normalne. Kto zatrzyma danie.


}
catch(Exception e)
2

Obecny trend sprawdzania poprawnoci w komentarzach przez rodowiska IDE jest zbawieniem dla wszystkich, ktrzy
czytaj duo kodu.

86

ROZDZIA 4.

{
try
{
response.add(ErrorResponder.makeExceptionString(e));
response.closeAll();
}
catch(Exception e1)
{

// Musz zrobi przerw!


}
}
}

Zamiast szuka ukojenia w bezuytecznych komentarzach, programista powinien zauway, e jego frustracja moe by rozadowana przez poprawienie struktury kodu. Powinien skierowa swoj
energi na wyodrbnienie ostatniego bloku try-catch do osobnej funkcji, jak jest to pokazane na
listingu 4.5.
L I S T I N G 4 . 5 . startSending (zmodyfikowany)
private void startSending()
{
try
{
doSending();
}
catch(SocketException e)
{

// Normalne. Kto zatrzyma danie.


}
catch(Exception e)
{
addExceptionAndCloseResponse(e);
}
}
private void addExceptionAndCloseResponse(Exception e)
{
try
{
response.add(ErrorResponder.makeExceptionString(e));
response.closeAll();
}
catch(Exception e1)
{
}
}

Warto zastpi pokus tworzenia szumu determinacj do wyczyszczenia swojego kodu. Pozwala to
sta si lepszym i szczliwszym programist.

Przeraajcy szum
Komentarze Javadoc rwnie mog by szumem. Jakie jest przeznaczenie poniszych komentarzy
Javadoc (ze znanej biblioteki open source)? Odpowied: adne. S to po prostu nadmiarowe komentarze stanowice szum informacyjny, napisane w le pojtej chci zapewnienia dokumentacji.

KOMENTARZE

87

/** Nazwa. */
private String name;

/** Wersja. */
private String version;

/** nazwaLicencji. */
private String licenceName;

/** Wersja. */
private String info;

Przeczytajmy dokadniej te komentarze. Czy czytelnik moe zauway bd kopiowania i wklejania? Jeeli autor nie powici uwagi pisaniu komentarzy (lub ich wklejaniu), to czy czytelnik moe
oczekiwa po nich jakiej korzyci?

Nie uywaj komentarzy, jeeli mona uy funkcji lub zmiennej


Przeanalizujmy poniszy fragment kodu:
// Czy modu z listy globalnej <mod> zaley
// od podsystemu, ktrego jest czci?
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))

Moe to by przeorganizowane bez uycia komentarzy:


ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))

Autor oryginalnego kodu prawdopodobnie napisa komentarz na pocztku (niestety), a nastpnie


kod realizujcy zadanie z komentarza. Jeeli jednak autor zmodyfikowaby kod w sposb, w jaki ja
to wykonaem, komentarz mgby zosta usunity.

Znaczniki pozycji
Czasami programici lubi zaznacza okrelone miejsca w pliku rdowym. Na przykad ostatnio
trafiem na program, w ktrym znalazem co takiego:
// Akcje //////////////////////////////////

Istniej rzadkie przypadki, w ktrych sensowne jest zebranie okrelonych funkcji razem pod tego
rodzaju transparentami. Jednak zwykle powoduj one chaos, ktry powinien by wyeliminowany
szczeglnie ten pocig ukonikw na kocu.
Transparent ten jest zaskakujcy i oczywisty, jeeli nie widzimy go zbyt czsto. Tak wic warto
uywa ich oszczdnie i tylko wtedy, gdy ich zalety s wyrane. Jeeli zbyt czsto uywamy tych
transparentw, zaczynaj by traktowane jako szum ta i ignorowane.

Komentarze w klamrach zamykajcych


Zdarza si, e programici umieszczaj specjalne komentarze po klamrach zamykajcych, tak jak
na listingu 4.6. Cho moe to mie sens w przypadku dugich funkcji, z gboko zagniedonymi
strukturami, w maych i hermetycznych funkcjach, jakie preferujemy, tworz tylko niead. Jeeli wic
Czytelnik bdzie chcia oznacza klamry zamykajce, niech sprbuje zamiast tego skrci funkcj.

88

ROZDZIA 4.

L I S T I N G 4 . 6 . wc.java
public class wc {
public static void main(String[] args) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line;
int lineCount = 0;
int charCount = 0;
int wordCount = 0;
try {
while ((line = in.readLine()) != null) {
lineCount++;
charCount += line.length();
String words[] = line.split("\\W");
wordCount += words.length;
} //while
System.out.println("wordCount = " + wordCount);
System.out.println("lineCount = " + lineCount);
System.out.println("charCount = " + charCount);
} // try
catch (IOException e) {
System.err.println("Error:" + e.getMessage());
} //catch
} //main
}

Atrybuty i dopiski
/* Dodane przez Ricka */

Systemy kontroli wersji wietnie nadaj si do zapamitywania, kto (i kiedy) doda okrelony
fragment. Nie ma potrzeby zamiecania kodu tymi maymi dopiskami. Mona uwaa, e tego typu komentarze bd przydatne do sprawdzenia, z kim mona porozmawia na temat danego fragmentu kodu. Rzeczywisto jest inna zwykle zostaj tam przez lata, tracc na dokadnoci i uytecznoci.
Pamitajmy systemy kontroli wersji s lepszym miejscem dla tego rodzaju informacji.

Zakomentowany kod
Niewiele jest praktyk tak nieprofesjonalnych, jak zakomentowanie kodu. Nie rb tego!
InputStreamResponse response = new InputStreamResponse();
response.setBody(formatter.getResultStream(), formatter.getByteCount());

// InputStream resultsStream = formatter.getResultStream();


// StreamReader reader = new StreamReader(resultsStream);
// response.setContent(reader.read(formatter.getByteCount()));

Inni programici, ktrzy zobacz taki zakomentowany kod, nie bd mieli odwagi go usun. Uznaj,
e jest tam z jakiego powodu i e jest zbyt wany, aby go usun. W ten sposb zakomentowany
kod zaczyna si odkada jak osad na dnie butelki zepsutego wina.

KOMENTARZE

89

Przeanalizujmy fragment z projektu Apache:


this.bytePos = writeBytes(pngIdBytes, 0);

//hdrPos = bytePos;
writeHeader();
writeResolution();

//dataPos = bytePos;
if (writeImageData()) {
writeEnd();
this.pngBytes = resizeByteArray(this.pngBytes, this.maxPos);
}
else {
this.pngBytes = null;
}
return this.pngBytes;

Dlaczego te dwa wiersze kodu s zakomentowane? Czy s wane? Czy jest to pozostao po wczeniejszych zmianach? Czy te s bdami, ktre kto przed laty zakomentowa i nie zada sobie trudu, aby to wyczyci?
W latach szedziesitych ubiegego wieku komentowanie kodu mogo by przydatne. Jednak od
bardzo dugiego czasu mamy ju dobre systemy kontroli wersji. Systemy te pamitaj za nas wczeniejszy kod. Nie musimy ju komentowa kodu. Po prostu moemy go usun. Nie stracimy go.
Gwarantuj.

Komentarze HTML
Kod HTML w komentarzach do kodu rdowego jest paskudny, o czym mona si przekona po
przeczytaniu kodu zamieszczonego poniej. Powoduje on, e komentarze s trudne do przeczytania w jedynym miejscu, gdzie powinny by atwe do czytania edytorze lub rodowisku IDE. Jeeli komentarze maj by pobierane przez jakie narzdzie (na przykad Javadoc), aby mogy by
wywietlone na stronie WWW, to zadaniem tego narzdzia, a nie programisty, powinno by opatrzenie ich stosownymi znacznikami HTML.
/**
* Zadanie uruchomienia testw sprawnoci.
* Zadanie uruchamia testy fitnesse i publikuje wyniki.
* <p/>
* <pre>
* Zastosowanie:
* &lt;taskdef name=&quot;execute-fitnesse-tests&quot;
* classname=&quot;fitnesse.ant.ExecuteFitnesseTestsTask&quot;
* classpathref=&quot;classpath&quot; /&gt;
* LUB
* &lt;taskdef classpathref=&quot;classpath&quot;
* resource=&quot;tasks.properties&quot; /&gt;
* <p/>
* &lt;execute-fitnesse-tests
* suitepage=&quot;FitNesse.SuiteAcceptanceTests&quot;
* fitnesseport=&quot;8082&quot;
* resultsdir=&quot;${results.dir}&quot;
* resultshtmlpage=&quot;fit-results.html&quot;
* classpathref=&quot;classpath&quot; /&gt;
* </pre>
*/

90

ROZDZIA 4.

Informacje nielokalne
Jeeli konieczne jest napisanie komentarza, to naley upewni si, e opisuje on kod znajdujcy si
w pobliu. Nie naley udostpnia informacji dotyczcych caego systemu w kontekcie komentarzy lokalnych. Wemy jako przykad zamieszczone poniej komentarze Javadoc. Pomijajc fakt, e
s zupenie zbdne, zawieraj one informacje o domylnym porcie. Funkcja jednak nie ma absolutnie adnej kontroli nad t wartoci domyln. Komentarz nie opisuje funkcji, ale inn cz
systemu, znacznie od niej oddalon. Oczywicie, nie ma gwarancji, e komentarz ten zostanie
zmieniony, gdy kod zawierajcy warto domyln ulegnie zmianie.
/**
* Port, na ktrym dziaa fitnesse. Domylnie <b>8082</b>.
*
* @param fitnessePort
*/
public void setFitnessePort(int fitnessePort)
{
this.fitnessePort = fitnessePort;
}

Nadmiar informacji
Nie naley umieszcza w komentarzach interesujcych z punktu widzenia historii dyskusji lub lunych opisw szczegw. Komentarz zamieszczony poniej zosta pobrany z moduu majcego za
zadanie sprawdzi, czy funkcja moe kodowa i dekodowa zgodnie ze standardem base64. Osoba
czytajca ten kod nie musi zna wszystkich szczegowych informacji znajdujcych si w komentarzu,
poza numerem RFC.
/*
RFC 2045 - Multipurpose Internet Mail Extensions (MIME)
Part One: Format of Internet Message Bodies
section 6.8. Base64 Content-Transfer-Encoding
The encoding process represents 24-bit groups of input bits as output
strings of 4 encoded characters. Proceeding from left to right,
a 24-bit input group is formed by concatenating 3 8-bit input groups.
These 24 bits are then treated as 4 concatenated 6-bit groups, each
of which is translated into a single digit in the base64 alphabet.
When encoding a bit stream via the base64 encoding, the bit stream
must be presumed to be ordered with the most-significant-bit first.
That is, the first bit in the stream will be the high-order bit in
the first 8-bit byte, and the eighth bit will be the low-order bit in
the first 8-bit byte, and so on.
*/

Nieoczywiste poczenia
Poczenie pomidzy komentarzem a kodem, ktry on opisuje, powinno by oczywiste. Jeeli mamy
problemy z napisaniem komentarza, to powinnimy przynajmniej doprowadzi do tego, by czytelnik patrzcy na komentarz i kod rozumia, o czym mwi dany komentarz.

KOMENTARZE

91

Jako przykad wemy komentarz zaczerpnity z projektu Apache:


/*
* Zaczynamy od tablicy, ktra jest na tyle dua, aby zmieci wszystkie piksele
* (plus filter bajtw) oraz dodatkowe 200 bajtw na informacje nagwka.
*/
this.pngBytes = new byte[((this.width + 1) * this.height * 3) + 200];

Co to s bajty filter? Czy ma to jaki zwizek z wyraeniem +1? A moe z *3? Z obydwoma? Czy
piksel jest bajtem? Dlaczego 200? Zadaniem komentarza jest wyjanianie kodu, ktry sam si nie objania. Jaka szkoda, e sam komentarz wymaga dodatkowego objanienia.

Nagwki funkcji
Krtkie funkcje nie wymagaj rozbudowanych opisw. Odpowiednio wybrana nazwa maej funkcji
realizujcej jedn operacj jest zwykle lepsza ni nagwek z komentarzem.

Komentarze Javadoc w niepublicznym kodzie


Komentarze Javadoc s przydatne w publicznym API, ale za to niemile widziane w kodzie nieprzeznaczonym do publicznego rozpowszechniania. Generowanie stron Javadoc dla klas i funkcji wewntrz systemu zwykle nie jest przydatne, a dodatkowy formalizm komentarzy Javadoc przyczynia
si jedynie do powstania bdw i rozproszenia uwagi.

Przykad
Kod zamieszczony na listingu 4.7 zosta przeze mnie napisany na potrzeby pierwszego kursu XP
Immersion. By on w zamierzeniach przykadem zego stylu kodowania i komentowania. Pniej
Kent Beck przebudowa go do znacznie przyjemniejszej postaci na oczach kilkudziesiciu entuzjastycznie reagujcych studentw. Pniej zaadaptowaem ten przykad na potrzeby mojej ksiki
Agile Software Development, Principles, Patterns, and Practices i pierwszych artykuw Craftman
publikowanych w magazynie Software Development.
Fascynujce w tym module jest to, e swego czasu byby on uznawany za dobrze udokumentowany.
Teraz postrzegamy go jako may baagan. Spjrzmy, jak wiele problemw z komentarzami mona
tutaj znale.
L I S T I N G 4 . 7 . GeneratePrimes.java
/**
* Klasa ta generuje liczby pierwsze do okrelonego przez uytkownika
* maksimum. Uytym algorytmem jest sito Eratostenesa.
* <p>
* Eratostenes z Cyrene, urodzony 276 p.n.e. w Cyrene, Libia -* zmar 194 p.n.e. w Aleksandrii. Pierwszy czowiek, ktry obliczy
* obwd Ziemi. Znany rwnie z prac nad kalendarzem
* z latami przestpnymi i prowadzenia biblioteki w Aleksandrii.
* <p>
* Algorytm jest dosy prosty. Mamy tablic liczb cakowitych
* zaczynajcych si od 2. Wykrelamy wszystkie wielokrotnoci 2. Szukamy

92

ROZDZIA 4.

* nastpnej niewykrelonej liczby i wykrelamy wszystkie jej wielokrotnoci.


* Powtarzamy dziaania do momentu osignicia pierwiastka kwadratowego z maksymalnej wartoci.
*
* @author Alphonse
* @version 13 Feb 2002 atp
*/
import java.util.*;
public class GeneratePrimes
{

/**
* @param maxValue jest limitem generacji.
*/
public static int[] generatePrimes(int maxValue)
{
if (maxValue >= 2) // Jedyny prawidowy przypadek.
{

// Deklaracje.
int s = maxValue + 1; // Rozmiar tablicy.
boolean[] f = new boolean[s];
int i;

// Inicjalizacja tablicy wartociami true.


for (i = 0; i < s; i++)
f[i] = true;

// Usuwanie znanych liczb niebdcych pierwszymi.


f[0] = f[1] = false;

// Sito.
int j;
for (i = 2; i < Math.sqrt(s) + 1; i++)
{
if (f[i]) // Jeeli i nie jest wykrelone, wykrelamy jego wielokrotnoci.
{
for (j = 2 * i; j < s; j += i)
f[j] = false; // Wielokrotnoci nie s pierwsze.
}
}

// Ile mamy liczb pierwszych?


int count = 0;
for (i = 0; i < s; i++)
{
if (f[i])
count++; // Licznik trafie.
}
int[] primes = new int[count];

// Przeniesienie liczb pierwszych do wyniku.


for (i = 0, j = 0; i < s; i++)
{
if (f[i]) // Jeeli pierwsza.
primes[j++] = i;
}
return primes; // Zwracamy liczby pierwsze.
}
else // maxValue < 2
return new int[0]; // Zwracamy pust tablic, jeeli niewaciwe dane wejciowe.
}
}

KOMENTARZE

93

Na listingu 4.8 zamieszczona jest przebudowana wersja tego samego moduu. Warto zauway, e
znacznie ograniczona jest liczba komentarzy. W caym module znajduj si tylko dwa komentarze.
Oba s z natury opisowe.
L I S T I N G 4 . 8 . PrimeGenerator.java (przebudowany)
/**
* Klasa ta generuje liczby pierwsze do okrelonego przez uytkownika
* maksimum. Uytym algorytmem jest sito Eratostenesa.
* Mamy tablic liczb cakowitych zaczynajcych si od 2.
* Wyszukujemy pierwsz nieokrelon liczb i wykrelamy wszystkie jej
* wielokrotnoci. Powtarzamy, a nie bdzie wicej wielokrotnoci w tablicy.
*/
public class PrimeGenerator
{
private static boolean[] crossedOut;
private static int[] result;
public static int[] generatePrimes(int maxValue)
{
if (maxValue < 2)
return new int[0];
else
{
uncrossIntegersUpTo(maxValue);
crossOutMultiples();
putUncrossedIntegersIntoResult();
return result;
}
}
private static void uncrossIntegersUpTo(int maxValue)
{
crossedOut = new boolean[maxValue + 1];
for (int i = 2; i < crossedOut.length; i++)
crossedOut[i] = false;
}
private static void crossOutMultiples()
{
int limit = determineIterationLimit();
for (int i = 2; i <= limit; i++)
if (notCrossed(i))
crossOutMultiplesOf(i);
}
private static int determineIterationLimit()
{

// Kada wielokrotno w tablicy ma podzielnik bdcy liczb pierwsz


// mniejsz lub rwn pierwiastkowi kwadratowemu wielkoci tablicy,
// wic nie musimy wykrela wielokrotnoci wikszych od tego pierwiastka.
double iterationLimit = Math.sqrt(crossedOut.length);
return (int) iterationLimit;
}
private static void crossOutMultiplesOf(int i)
{
for (int multiple = 2*i;
multiple < crossedOut.length;
multiple += i)
crossedOut[multiple] = true;

94

ROZDZIA 4.

}
private static boolean notCrossed(int i)
{
return crossedOut[i] == false;
}
private static void putUncrossedIntegersIntoResult()
{
result = new int[numberOfUncrossedIntegers()];
for (int j = 0, i = 2; i < crossedOut.length; i++)
if (notCrossed(i))
result[j++] = i;
}
private static int numberOfUncrossedIntegers()
{
int count = 0;
for (int i = 2; i < crossedOut.length; i++)
if (notCrossed(i))
count++;
return count;
}
}

Mona si spiera, e pierwszy komentarz jest nadmiarowy, poniewa czyta si go podobnie jak
sam funkcj genratePrimes. Uwaam jednak, e komentarz uatwia czytelnikowi poznanie algorytmu, wic zdecydowaem o jego pozostawieniu.
Drugi komentarz jest niemal na pewno niezbdny. Wyjania powody zastosowania pierwiastka
jako ograniczenia ptli. Mona sprawdzi, e adna z prostych nazw zmiennych ani inna struktura
kodu nie pozwala na wyjanienie tego punktu. Z drugiej strony, uycie pierwiastka moe by prnoci. Czy faktycznie oszczdzam duo czasu przez ograniczenie liczby iteracji do pierwiastka
liczby? Czy obliczenie pierwiastka nie zajmuje wicej czasu, ni uda si nam zaoszczdzi?
Warto o tym pomyle. Zastosowanie pierwiastka jako limitu ptli zadowala siedzcego we mnie
eksperta C i asemblera, ale nie jestem przekonany, e jest to warte czasu i energii osoby, ktra ma
za zadanie zrozumie ten kod.

Bibliografia
[KP78]: Kernighan i Plaugher, The Elements of Programming Style, McGraw-Hill 1978.

KOMENTARZE

95

You might also like