Professional Documents
Culture Documents
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
SPIS TRECI
Sowo wstpne
13
Wstp
19
1. Czysty kod
23
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
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
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
124
125
126
127
127
SPIS TRECI
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
178
181
182
183
183
184
184
185
187
187
188
188
189
191
192
192
192
13. Wspbieno
193
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
209
210
216
228
261
263
264
276
277
Na pocztek uruchamiamy
Teraz poprawiamy
Zakoczenie
Bibliografia
278
280
293
294
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
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
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
[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;
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.
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*");
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);
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()
{
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();
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)
{
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.
/**
* 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 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)
{
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)
{
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)
{
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?
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.
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());
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
//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:
* <taskdef name="execute-fitnesse-tests"
* classname="fitnesse.ant.ExecuteFitnesseTestsTask"
* classpathref="classpath" />
* LUB
* <taskdef classpathref="classpath"
* resource="tasks.properties" />
* <p/>
* <execute-fitnesse-tests
* suitepage="FitNesse.SuiteAcceptanceTests"
* fitnesseport="8082"
* resultsdir="${results.dir}"
* resultshtmlpage="fit-results.html"
* classpathref="classpath" />
* </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
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.
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.
/**
* @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;
// 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.
}
}
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()
{
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