Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00102 009737 11027979 na godz. na dobę w sumie
Sztuka pisania oprogramowania. Wybór i redakcja Joel Spolsky - książka
Sztuka pisania oprogramowania. Wybór i redakcja Joel Spolsky - książka
Autor: Liczba stron: 328
Wydawca: Helion Język publikacji: polski
ISBN: 83-246-0464-2 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> techniki programowania
Porównaj ceny (książka, ebook, audiobook).

Programowanie to nie tylko wiedza -- to także sztuka. Aplikacja jest narzędziem, które powinno być przede wszystkim użyteczne i ergonomiczne. Niestety, wielu twórców oprogramowania zapomina o tym, pisząc swoje programy. Powodów jest wiele: zbyt mało czasu, źle sformułowane założenia, nieprawidłowa komunikacja między członkami zespołu projektowego czy też niestosowanie się do konwencji kodowania i testowania. Niezależnie od przyczyn, konsekwencją jest oprogramowanie, które nie spełnia swojej podstawowej funkcji, jaką jest użyteczność.

Po przeczytaniu książki 'Sztuka pisania oprogramowania. Wybór i redakcja Joel Spolsky' spojrzysz na proces tworzenia aplikacji w inny sposób. Czytając zawarte w niej artykuły autorstwa doświadczonych programistów i menedżerów, dowiesz się, jak prowadzić projekty informatyczne, czego unikać i jakie techniki stosować. Znajdziesz w niej opracowania dotyczące stylu kodowania, zarządzania projektem, testowania, korzystania z nieudokumentowanych właściwości systemu oraz zatrudniania programistów. Nieważne, czy jesteś programistą, czy kierownikiem projektu, dowiesz się z niej wielu interesujących rzeczy.

W książce poruszono między innymi:

Jeśli chcesz podnieść jakość tworzonego oprogramowania,
koniecznie przeczytaj tę książkę.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

IDZ DO IDZ DO PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ SPIS TREœCI SPIS TREœCI KATALOG KSI¥¯EK KATALOG KSI¥¯EK KATALOG ONLINE KATALOG ONLINE ZAMÓW DRUKOWANY KATALOG ZAMÓW DRUKOWANY KATALOG TWÓJ KOSZYK TWÓJ KOSZYK DODAJ DO KOSZYKA DODAJ DO KOSZYKA CENNIK I INFORMACJE CENNIK I INFORMACJE ZAMÓW INFORMACJE ZAMÓW INFORMACJE O NOWOœCIACH O NOWOœCIACH ZAMÓW CENNIK ZAMÓW CENNIK CZYTELNIA CZYTELNIA FRAGMENTY KSI¥¯EK ONLINE FRAGMENTY KSI¥¯EK ONLINE Wydawnictwo Helion ul. Koœciuszki 1c 44-100 Gliwice tel. 032 230 98 63 e-mail: helion@helion.pl Sztuka pisania oprogramowania. Wybór i redakcja Joel Spolsky Autor: Joel Spolsky T³umaczenie: Andrzej Gra¿yñski ISBN: 83-246-0464-2 Tytu³ orygina³u: The Best Software Writing I: Selected and Introduced by Joel Spolsky Format: B5, stron: 320 Programowanie to nie tylko wiedza — to tak¿e sztuka. Aplikacja jest narzêdziem, które powinno byæ przede wszystkim u¿yteczne i ergonomiczne. Niestety, wielu twórców oprogramowania zapomina o tym, pisz¹c swoje programy. Powodów jest wiele: zbyt ma³o czasu, Ÿle sformu³owane za³o¿enia, nieprawid³owa komunikacja miêdzy cz³onkami zespo³u projektowego czy te¿ niestosowanie siê do konwencji kodowania i testowania. Niezale¿nie od przyczyn, konsekwencj¹ jest oprogramowanie, które nie spe³nia swojej podstawowej funkcji, jak¹ jest u¿ytecznoœæ. Po przeczytaniu ksi¹¿ki „Sztuka pisania oprogramowania. Wybór i redakcja Joel Spolsky” spojrzysz na proces tworzenia aplikacji w inny sposób. Czytaj¹c zawarte w niej artyku³y autorstwa doœwiadczonych programistów i mened¿erów, dowiesz siê, jak prowadziæ projekty informatyczne, czego unikaæ i jakie techniki stosowaæ. Znajdziesz w niej opracowania dotycz¹ce stylu kodowania, zarz¹dzania projektem, testowania, korzystania z nieudokumentowanych w³aœciwoœci systemu oraz zatrudniania programistów. Niewa¿ne, czy jesteœ programist¹, czy kierownikiem projektu, dowiesz siê z niej wielu interesuj¹cych rzeczy. W ksi¹¿ce poruszono miêdzy innymi: (cid:129) Styl kodowania (cid:129) Projektowanie interfejsów u¿ytkownika (cid:129) Pu³apki outsourcingu (cid:129) W³aœciwe procedury testowania (cid:129) Unikanie nadmiernie rozbudowanych procesów decyzyjnych (cid:129) Zasady pracy z zespo³em projektowym (cid:129) Dobór odpowiednich pracowników Jeœli chcesz podnieœæ jakoœæ tworzonego oprogramowania, koniecznie przeczytaj tê ksi¹¿kê SPIS TREŚCI Ken Arnold Ken Bambrick O redaktorze ...................................................7 O autorach ......................................................9 Wprowadzenie ............................................. 17 Styl jest istotny .............................................. 21 Nominacja do nagrody za najgłupszy interfejs użytkownika: narzędzie wyszukiwania Windows ................................ 29 Pułapki programistycznego outsourcingu. Dlaczego niektóre firmy programistyczne mylą pudełko z czekoladkami? ...................... 31 Excel jako baza danych ................................. 39 Prosto, zwyczajnie, po ludzku ....................... 45 Autystyczne oprogramowanie społeczne ....... 57 Dlaczego nie blokować programów wykorzystujących nieudokumentowane mechanizmy? ............................................... 69 Kevin Cheng i Tom Chi Kopanie lamy ............................................... 73 Michael Bean Rory Blyth Adam Bosworth Danah Boyd Raymond Chen 6 SZTUKA PISANIA OPROGRAMOWANIA Cory Doctorow ea_spouse Bruce Eckel Paul Ford Paul Graham John Gruber Gregor Hohpe John Jeffries Eric Johnson Eric Lippert Boże, zachowaj kanadyjski internet od WIPO .. 75 EA: ludzka historia ........................................ 81 Rygorystyczna kontrola typów kontra rygorystyczne testowanie .............................. 89 Processing .................................................. 101 Wielcy hakerzy ........................................... 117 Gdy pole URL staje się wierszem poleceń ... 133 Dlaczego w Starbuck® nie korzysta się z potwierdzania dwufazowego? .................. 141 Pasja ........................................................... 147 C++ — zapomniany koń trojański ............. 151 Ilu pracowników Microsoftu potrzeba do wymiany żarówki? ................................. 157 Michael „Rands” Lopp Co robić, gdy zostaniesz wkręcony? Larry Osterman Mary Poppendieck Rick Schaut Clay Shirky Clay Shirky Eric Sink Eric Sink Eric Sink Aaron Swartz why the lucky stiff 5 scenariuszy dla pracowitych dyrektorów technicznych ............................ 161 Reguła tworzenia oprogramowania nr 2: pomiar wydajności testerów za pomocą metryk ilościowych nie zdaje egzaminu ....... 173 Kompensacja zespołowa ............................. 179 Mac Word 6.0 .............................................193 Grupa sama dla siebie jest największym wrogiem ........................... 203 Grupa jako użytkownik: flamewars a projektowanie oprogramowania społecznego .................................................229 Zamykanie luki, część 1. ............................. 241 Zamykanie luki, część 2. ............................. 251 Loteria zatrudniania .................................... 265 PowerPoint — remiks ................................. 279 Krótka, ilustrowana i (mam nadzieję) bezstresowa wycieczka po języku Ruby ........285 Skorowidz .................................................. 311 R o z d z i a ł 1 1 Bruce Eckel RYGORYSTYCZNA KONTROLA TYPÓW KONTRA RYGORYSTYCZNE TESTOWANIE1 Od redakcji: Pamiętam, że gdy pracowaliśmy w Microsofcie nad VBA, toczyli- śmy niekończące się dyskusje na temat statycznej i dynamicznej kontroli typów w programach. Ze statyczną kontrolą typów mamy do czynienia wówczas, gdy weryfikacja poprawności typów wszystkich zmiennych przeprowadzana jest na etapie kompilacji programu. Jeżeli na przykład w programie znajduje się funkcja log(), która wymaga liczby rzeczywistej jako jedynego parametru, wywołanie tej funkcji w postaci log( foo ) spowoduje sygnalizację błędu w rodzaju „tu oczekuje się liczby rzeczywistej” lub podobnego. Konsekwencją użycia 1 Bruce Eckel, „Strong Typing vs. Strong Testing”, Thinking About Computing, artykuły Bruce Eckela na MindView.net (http://www.MindView.net), 2 maja 2003. Patrz http://mindview.net/WebLog/log-0025. 9 0 SZTUKA PISANIA OPROGRAMOWANIA niewłaściwego typu — łańcucha zamiast liczby — jest niemożność skom- pilowania programu. Dla odróżnienia, w ramach dynamicznej kontroli typów weryfikacja tychże odbywa się w czasie wykonania programu. Konstrukcja log( foo ) zosta- nie skomplikowana, a jej niepoprawność okaże się faktem dopiero w trakcie wykonania programu. Podejście to ma tę oczywistą wadę, iż fakt ten może stać się wiadomy dopiero po kilku miesiącach czy latach eksploatacji pro- gramu, zwłaszcza jeżeli wzmiankowane wywołanie znajduje się wewnątrz funkcji wywoływanej bardzo rzadko. Jako że projektowany VBA miał być z założenia językiem skryptowym dla Excela, osobiście optowałem za kontrolą dynamiczną. Jest ona koncepcyjnie prostsza dla użytkowników niebędących programistami, którzy mogą mieć trudności ze zrozumieniem pojęcia zmiennej, nie mówiąc już o pojęciu typu. Miałem wówczas po swej stronie wielu użytkowników Smalltalka argu- mentujących (raczej ogólnikowo): „Wciąż szukasz przyczyny problemu, więc lepiej byłoby, żebyś poznał ją w ciągu kilku sekund”. Często okazuje się to prawdą, jednak nie zawsze. Ostatecznie, po długich debatach, udało mi się przekonać innych do moich racji i tak narodził się typ Variant — struktura zdolna przechowywać wartości dowolnego typu — jako element VBA i COM oraz jako jedyny dopuszczalny typ późniejszego języka skryptowego VBS. Nie zapominam oczywiście, że rygorystyczna kontrola typów dokonywana w czasie kompilacji jest wspaniałą rzeczą, umożliwia bowiem wczesne wy- krycie wielu błędów, co dla mnie, jako użytkownika C++, jest sprawą bez- dyskusyjną. Jeżeli na przykład tworzysz oprogramowanie dla przedsiębior- stwa, w którym jedynie menedżerowie uprawnieni są do otrzymywania nagród, możesz zdefiniować dwie różne struktury danych — dla pracowników i menedżerów — i tylko drugą z tych struktur wyposażyć w metodę PayBonus(). Wywołanie tej metody na rzecz rekordu reprezentującego szarego pracow- nika, nie szacownego menedżera, będzie niemożliwe, bo nie pozwoli na to kompilator. Problem w tym, iż tworzenie typów danych — jako samoistnych bytów — tylko po to, by częściowe testowanie programu przeprowadzone zostało już na etapie kompilacji, jest pomysłem po trosze niefortunnym. Owo „częściowe testowanie” może bowiem obejmować jedynie testy o charakterze general- nym, w rodzaju „czy mogę zrobić z tym obiektem to a to”, nie zaś testy bardziej szczegółowe w rodzaju „czy ta funkcja zwróci wartość 2,12 jeśli parametry jej wywołania będą miały postać 1, 32 i aardvark . W efekcie rygorystyczna kontrola typów — jako jeden z mechanizmów weryfi- kujących pewien tylko aspekt poprawności programu — może okazywać się BRUCE ECKEL 9 1 dla programisty w pewnym stopniu kłopotliwa. Staje się oczywiste, że w celu dogłębnej weryfikacji tej poprawności powinniśmy posłużyć się narzędziem bardziej funkcjonalnym i bezpośrednim — testami modułów. Prezentowa- ny przez autora pogląd, iż powinny one stać się zastępnikiem rygorystycznej kontroli typów na etapie kompilacji, jest ideą samą w sobie intrygującą. Zanim jednak oddam głos Bruce’owi, muszę zwrócić uwagę na jedną istotną rzecz. Otóż dynamiczna kontrola typów — czyli weryfikacja ich poprawno- ści w czasie wykonywania programu — wiąże się ze spadkiem efektywności wykonywania programu. Wielkość tego spadku jest oczywiście różna dla różnych programów, generalnie jednak języki stosujące kontrolę dynamiczną okazują się wolniejsze od swych odpowiedników opartych na statycznej kontroli. Wykorzystuję na co dzień program do odfiltrowywania spamu, napisany w języku Python i niekiedy „oflagowanie” pojedynczej wiadomości wymaga kilku sekund, tak że zakwalifikowanie jako spam 10 czy 20 wiado- mości może już oznaczać oczekiwanie przez kilka minut. Taka jest cena dynamicznej kontroli typów, w przypadku dużej „farmy” serwerowej przy- bierająca konkretne rozmiary perspektywy pięcio- lub dziesięciokrotnego zwiększenia liczby serwerów w celu utrzymania wydajności systemu na akceptowalnym poziomie. Trudno więc jednoznacznie wskazać wyraźną przewagę któregoś z opisa- nych sposobów — statycznego czy dynamicznego — kontroli typów danych w programie. Nawet bowiem jeśli testy modułów dają możliwość wszech- stronnego weryfikowania poprawności programów, to jednak nie można wpadać w przesadę, optując na rzecz całkowitego zaniechania kontroli typów przez kompilatory. O d pewnego czasu szczególnym przedmiotem mego zainteresowania jest produktywność programistów. Czas programisty jest drogi, czas kompu- tera — niemal darmowy, nie ma więc żadnego powodu, by płacić tym pierwszym za ten drugi. Jak możemy maksymalnie ułatwić sobie rozwiązanie konkretnego proble- mu? Gdy tylko pojawia się nowe narzędzie (głównie — nowy język progra- mowania), dostarcza ono pewnego poziomu abstrakcji, który może, lecz nie musi, skrywać przed programistą pewne nieistotne szczegóły. Osiągnięcie tej abstrakcji wymaga jednak niekiedy tyle wysiłku, iż programista często gotów byłby sprzymierzyć się (niczym doktor Faust) z samym diabłem, by tylko mieć ten wysiłek już za sobą. Koronnym przykładem takiego języka jest Perl: jego 9 2 SZTUKA PISANIA OPROGRAMOWANIA bezpośredniość uwalnia co prawda programistę od dużej dozy „programistycznej biurokracji”, lecz nieczytelna składnia (wzorowana na uniksowych narzę- dziach w stylu awk, sed i grep) z punktu widzenia produktywności programisty okazuje się fatalna. W ciągu kilku ostatnich lat wspomniany „faustowski targ” zdaje się przy- bierać postać bardziej namacalną — orientację tradycyjnych języków w kierunku statycznej kontroli typów. Zdecydowało to o mojej dwumiesięcznej przygo- dzie miłosnej z Perlem, który oznaczał całkowity zwrot w kategoriach mojej własnej produktywności jako programisty (płomienna miłość skończyła się równie szybko za sprawą karygodnego traktowania przez Perl referencji i klas; problemy ze składnią dały znać o sobie nieco później). Kwestię wyboru sta- tycznej albo dynamicznej kontroli typów trudno w kontekście Perla rozważać, jako że nie sposób budować w nim projektów dostatecznie dużych na to, by wynikające z tej kwestii problemy mogły naprawdę dawać znać o sobie. Po przesiadce na język Python (do pobrania za darmo z witryny www. Python.org) — język, w którym można tworzyć ogromne, skomplikowane systemy — coraz wyraźniej począłem konstatować, że mimo ewidentnej bez- troski w kategoriach kontroli typów tworzone w tym języku programy funkcjo- nują całkiem nieźle, a ich tworzenie wymaga niewielkiego wysiłku i wolne jest od tych wszystkich problemów, jakich moglibyśmy spodziewać się po języku pozbawionym statycznej kontroli typów. Kontroli, którą wielu zwykło uwa- żać za jedyny właściwy sposób rozwiązywania problemów programistycznych. Skoro jednak statyczna kontrola typów jest mechanizmem tak istotnym i tak nieodzownym, dlaczego ludzie w ogóle decydują się na używanie języka Python do tworzenia dużych, skomplikowanych systemów (w dodatku szybciej i przy znacznie mniejszym wysiłku niż w przypadku języków „statycznych”), wolnych od katastrofalnych zachowań, jakie (rzekomo) niechybnie powinny wystąpić? Powyższa kwestia zachwiała mym niewzruszonym dotąd przekonaniem do statycznej kontroli typów (nabytym w czasie przesiadki z wczesnych wersji języka C na C++, gdzie usprawnienie wielu mechanizmów było dramatyczne) i to do tego stopnia, że kwestionować począłem nawet istnienie weryfikowa- nych wyjątków (checked exceptions) w języku Java2 — co wywołało dyskusję tak burzliwą3, jakby moja obrona wyjątków nieweryfikowanych spowodować miała co najmniej zagładę cywilizacji ludzkiej. W książce Thinking in Java 2 Wyjątki weryfikowane są cechą języka polegającą na tym, że kompilator (w czasie kompilacji) dokonuje sprawdzenia, czy każda funkcja, mogąca potencjalnie generować konkretne wyjątki, posiada kod niezbędny do obsłużenia tychże lub przynajmniej zabezpieczający przed wydostaniem się ich na zewnątrz — przyp. red. 3 Patrz http://www.mindview.net/Etc/Discussions/CheckedExceptions. BRUCE ECKEL 9 3 (Prentice Hall PTR, wyd. trzecie, 2000)4 posunąłem się jeszcze dalej i zade- monstrowałem użycie wyjątku RuntimeException jako klasy-otoczki „eliminu- jącej” wyjątki weryfikowane. Każdorazowo, gdy używam tego mechanizmu, wydaje się on działać prawidłowo (gwoli sprawiedliwości winien jestem Czy- telnikom informację, iż Martin Fowler wpadł na ten sam pomysł mniej więcej w tym samym czasie co ja), mimo to wciąż otrzymuję listy upominające mnie, że szargam w ten sposób prawdę i uświęcone zasady i powinienem być ścigany na mocy USA Patriot5 (hej, chłopcy z FBI — witam w moim blogu). Stwierdzenie, że weryfikowane wyjątki niewarte są kłopotów, jakie mogą powodować (chodzi oczywiście o samo weryfikowanie, nie o wyjątki w ogólno- ści — jestem przekonany, że spójny, jednolity mechanizm raportowania błędów jest dla języka niezbędny), nie daje odpowiedzi na pytanie: „Dlaczego Python spisuje się tak dobrze, wszak zgodnie z naszymi konwencjonalnymi przekona- niami tworzone w nim programy powinny objawiać istne lawiny błędów?”. Python i języki jemu podobne podchodzą do kontroli typów w sposób zgoła leniwy: zamiast narzucać możliwie najwcześniej, możliwie najbardziej rygory- styczne ograniczenia na typy obiektów (jak czyni to Java), języki takie jak Ruby, Smalltalk i oczywiście Python maksymalnie tę kontrolę rozluźniają, przywiązując wagę do typu obiektów dopiero wtedy, kiedy naprawdę jest to konieczne. Koncepcja ta, zwana typowaniem spowolnionym (latent typing) lub typo- waniem strukturalnym (structural typing), określana bywa także potocznym mianem „kaczego typowania” (duck typing) — jest niemrawa niczym kaczy chód i jak kwakanie niewyraźna. Typowanie to oznacza w praktyce tyle, że można przesłać do obiektu dowolny komunikat, bez względu na jego typ. Gdy- byśmy na przykład chcieli zaprogramować w Javie dialog dwóch zwierzątek, mógłby on wyglądać mniej więcej tak: // zwierzęca konwersacja — wersja w Javie interface Pet { void speak(); } class Cat implements Pet { public void speak() { System.out.println( miau! ); } } 4 Dostępne jest wydanie polskie, szczegóły pod adresem http://helion.pl/ksiazki/thija3.htm — przyp. tłum. 5 USA Patriot Act — prawo amerykańskie ustanowione 26 października 2001 w wyniku zamachu na WTC. Na mocy tego prawa wolno przetrzymywać bez sądu, przez czas nieokreślony, obywateli nieamerykańskich, uznanych za zagrożenie dla bezpieczeństwa narodowego. Patrz http://pl.wikipedia.org/wiki/USA_Patriot_Act. — przyp. tłum. 9 4 SZTUKA PISANIA OPROGRAMOWANIA class Dog implements Pet { public void speak() { System.out.println( hau! ); } } public class PetSpeak { static void command(Pet p) { p.speak(); } public static void main(String[] args) { Pet[] pets = { new Cat(), new Dog() }; for(int i = 0; i pets.length; i++) command(pets[i]); } } Zwróćmy uwagę na ważny fakt, że w charakterze wywołania funkcji command() dopuszczalny jest jedynie obiekt określonego typu — Pet — reprezentującego dowolne (abstrakcyjne) zwierzę. Aby zatem zaprogramować zachowanie się konkretnych zwierząt (psa i kota), musimy z klasy Pet wyprowadzić reprezen- tujące je klasy (Dog i Cat) i w każdej z nich zdefiniować metodę speak(), wywo- dzącą się oryginalnie z typu Pet. Przez dłuższy czas uważałem, że przedstawione dziedziczenie metod jest przyrodzonym elementem programowania obiektowego (a odmienne zdanie użytkowników Smalltalka w tej kwestii bardzo mnie irytowało). Kiedy jednak zacząłem używać Pythona, zauważyłem bardzo ciekawą rzecz. Przetłumaczmy mianowicie prezentowany kod na język Python: # zwierzęca konwersacja — wersja w Pythonie class Pet: def speak(self): pass class Cat(Pet): def speak(self): print miau! class Dog(Pet): def speak(self): print hau! def command(pet): pet.speak() pets = [ Cat(), Dog() ] for pet in pets: command(pet) Jeśli nie widziałeś wcześniej Pythona, z pewnością stwierdzisz, iż definiuje on na nowo pojęcie języka zwięzłego, w bardzo pozytywnym znaczeniu. Czy uważasz C i C++ za języki zwięzłe? Wyrzućmy z nich nawiasy klamrowe — czytelne dla człowieka akapitowanie jest w Pythonie jednocześnie środkiem BRUCE ECKEL 9 5 do zaznaczania granic bloków. Typy argumentów i wyników funkcji? Zostawmy je samemu językowi. W instrukcjach tworzących klasy nazwy klas bazowych ujęte są w nawiasy. def oznacza definicję funkcji lub metody. Nie ma domyślnego parametru this reprezentującego obiekt wywołujący metodę, parametr ten trzeba specyfikować w sposób jawny i zgodnie z przyjętą konwencją nadaje mu się nazwę self. Słowo kluczowe pass oznacza odłożenie definicji na później i jako takie może być uważane za analogię słowa kluczowego abstract. Zwróćmy uwagę, że w wywołaniu command(pet) parametrem wywołania jest jakiś obiekt o nazwie pet, lecz nie sposób wywnioskować (na podstawie definicji funkcji command) niczego na temat typu tego obiektu. Jedyną rzeczą, jakiej się od tego obiektu wymaga, jest możliwość wywołania na jego rzecz metody speak(). Tak właśnie wygląda spowolnione typowanie, którego wybra- nymi aspektami zajmiemy się za chwilę. Kolejną rzeczą wartą wzmianki jest fakt, że funkcja command() jest zwykłą funkcją, nie metodą. W języku Python jest to rzecz naturalna, nie wszystko bowiem musi się w nim odbywać na modłę obiektową. Listy i słowniki (nazywane także mapami i tablicami skojarzeniowymi), tak ważne przy tworzeniu wielu programów, w języku Python uczynione zo- stały jego nierozerwalną częścią, ich używanie nie wymaga więc importowania jakichś specjalnych, dodatkowych bibliotek. Poniższa lista pets = [ Cat(), Dog() ] składa się z dwóch nowo utworzonych obiektów typu Cat i Dog. W celu ich utworzenia wywoływane są oczywiście odpowiednie konstruktory, lecz nie trzeba zaznaczać tego faktu explicite za pomocą słowa kluczowego new (w języku Java też nie jest ono tak naprawdę potrzebne, pozostało w nim jednak jako jeden z elementów schedy po C++). Rodzimym elementem Pythona jest także inna równie istotna operacja — iterowanie po sekwencji elementów: instrukcja for pet i pets: podstawia kolejne elementy listy pets pod zmienną pet. Rozwiązanie bardziej eleganckie niż w Javie, nawet w porównaniu z instrukcją foreach w wersji J2SE5. Wynik działania powyższego fragmentu jest identyczny z przedstawioną wcześniej wersją w Javie. Łatwo teraz zrozumieć, dlaczego Python jest często nazywany „wykonywalnym pseudokodem”. Nie tylko jest on wystarczająco pro- sty, by można go było używać jak pseudokodu, lecz ma tę wspaniałą cechę, iż tworzone fragmenty kodu można natychmiast wykonywać. W praktyce ozna- cza to, że używając języka Python można błyskawicznie wypróbowywać nowe 9 6 SZTUKA PISANIA OPROGRAMOWANIA pomysły i koncepcje, a gdy się już dostatecznie wykrystalizują, można kodo- wać je w Javie, C, C++ lub innym języku „z wyboru”. No dobrze, ale skoro właśnie rozwiązaliśmy konkretny problem w języku Python, po cóż jeszcze kłopotać się „przepisywaniem” rozwiązania na inny język? Na prowadzonych przeze mnie seminariach wielokrotnie używałem Pythona do kodowania przykładowych ćwiczeń, ponieważ pozwoliło mi to na pokazanie w sposób wy- raźny drogi, jaką sam dochodziłem do rozwiązania, a poza tym poprawność tworzonego sukcesywnie pseudokodu można było weryfikować na bieżąco, przez jego wykonywanie. Najbardziej jednak istotnym spostrzeżeniem w stosunku do prezentowane- go przed chwilą kodu jest to, że skoro funkcja command() nie troszczy się o typ swego parametru, można w ogóle zrezygnować z budowania hierarchii klas i z klasy bazowej Pet w szczególności: # zwierzęca konwersacja — wersja w Pythonie, bez klasy bazowej class Cat: def speak(self): print miau! class Dog: def speak(self): print hau! class Bob: def bow(self): print dziękuję serdecznie! def speak(self): print witam w moich skromnych progach! def drive(self): print piiiiiiiiiiip! def command(pet): pet.speak() pets = [ Cat(), Dog(), Bob() ] for pet in pets: command(pet) Ponieważ jedyną cechą obiektu pet, istotną z punktu widzenia wywołania command(pet), jest możliwość wysłania do tego obiektu komunikatu speak(), mogliśmy usunąć z programu klasę bazową Pet, a nawet dodać do niego klasę Bob, której z klasy Pet wyprowadzić niepodobna, ale która posiada metodę speak() i której obiekty z tego względu mogą być używane w charakterze para- metrów wywołania funkcji command(). Na widok powyższego zwolennicy statycznej kontroli typów zapewne do- staną apopleksji i z jeszcze większą stanowczością twierdzić będą, że takie BRUCE ECKEL 9 7 rozluźnienie reguł niechybnie doprowadzić musi do katastrofy. Korzyści wy- nikające z bardziej klarownego wyrażania koncepcji nie są warte ponoszonego ryzyka, nawet jeśli oznaczać mogą w praktyce pięcio- lub dziesięciokrotne zwiększenie produktywności programistów w porównaniu z Javą czy C++. Czy rzeczywiście? Czy przekazanie obiektu tam, gdzie nie powinien się on znaleźć, może istotnie powodować aż tak poważne problemy? Otóż w Pythonie wszelkie błędy raportowane są w postaci wyjątków, podobnie jak powinny być raportowane w Javie, C# i C++. Błędne użycie obiektu zostanie wykryte, tyle tylko, że stanie się to dopiero podczas wykonywania kodu zawierającego ów błąd. To kolejna woda na młyn zwolenników typowania statycznego, którzy twierdzą, że wobec tego nie można zagwarantować poprawności programu przed jego wykonaniem, bo kompilator nie wykonuje niezbędnej kontroli typów. Gdy pisałem książkę Thinking in C++ (Prentice Hall PTR, wyd. pierwsze, 1998)6 zastosowałem bardzo prostą metodę weryfikacji poprawności za- mieszczonych przykładów: napisałem program, który automatycznie wyłu- skuje przykładowy kod z treści książki (na podstawie odpowiednich marke- rów-komentarzy na początku i na końcu każdego fragmentu), po czym tworzy odpowiedni skrypt dla programu MAKE, umożliwiający skompilowa- nie wszystkich przykładów. W ten sposób mogłem zagwarantować, że cały przykładowy kod zamieszczony w książce jest akceptowany przez kompilator i że jako taki jest (tak wtedy myślałem) w pełni poprawny. Dręczące mnie wątpliwości, że przecież bezbłędne skompilowanie kodu nie oznacza jeszcze jego poprawnego działania, dziwnym trafem ignorowałem, dumny z faktu, że udało mi się zautomatyzować wielki krok na początku drogi do weryfikacji kodu (Czytelnicy książek o programowaniu nadto dobrze zdają sobie sprawę z faktu, że wielu autorów po prostu nie przejmuje się błędami w prezentowa- nym przez siebie kodzie). Gdy jednak w ciągu następnych lat okazywało się, że niektóre z przykładów działają niezgodnie z oczekiwaniami, nie mogłem dłużej ignorować konieczności testowania skompilowanych programów. Da- łem temu zresztą wyraz w trzecim wydaniu Thinking in Java, pisząc Jeśli coś nie jest przetestowane, jest niepoprawne Jeśli więc program napisany w języku o statycznej kontroli typów kompi- luje się bezbłędnie, oznacza to tylko tyle, że jest składniowo poprawny (kom- pilator Pythona także sprawdza składnię, tyle że jej reguły są znacznie mniej rygorystyczne). Składniowa poprawność programu to tylko jeden z aspektów 6 Dostępne jest wydanie polskie, szczegóły pod adresem http://helion.pl/ksiazki/thicpp.htm — przyp. tłum. 9 8 SZTUKA PISANIA OPROGRAMOWANIA poprawności rozumianej całościowo — jeśli kod zdaje się wyglądać na funk- cjonujący prawidłowo, wcale nie oznacza to, że w istocie będzie prawidłowo funkcjonował. Jedyną gwarancję poprawności — i to niezależnie od tego, czy kontrola typów ma charakter statyczny, czy dynamiczny — może dać tylko pomyślne zaliczenie wszystkich testów definiujących kryteria poprawności programu. Mowa tu o testach modułów, testach akceptacyjnych itd. W książce Thinking in Java, wyd. trzecie, znaleźć można mnóstwo takich testów, których trud tworzenia solennie się opłacił. Gdy tylko programista wyrobi w sobie odruch testowania każdego napisanego kodu (zostanie „zarażony testowaniem”, jak się popularnie mówi), nigdy już nie będzie w stanie zmienić swego podejścia do programowania. Przypominać to może przesiadkę z wczesnych wersji języka C na C++ — kompilator tego ostatniego wykonywał zdecydowanie więcej testów i tworzył kod bardziej efektywny. Na tym jednak rola każdego kompilatora musi się skończyć, bo nie ma on żadnej informacji odnośnie spodziewanego zacho- wania się programu; dalszą weryfikację poprawności mogą zapewnić jedy- nie wyczerpujące testy niezależnie od tego, w jakim języku tworzony jest pro- gram. Istnienie zestawu takich testów umożliwia weryfikowanie na bieżąco poprawności wszelkich zmian w kodzie (w ramach refaktoryzacji lub zmiany projektu), podobnie jak wykonanie kompilacji umożliwia weryfikację ich po- prawności składniowej. Tak więc niezależnie od wariantu typowania — statycznego albo dyna- micznego — w obliczu braku wspomnianych testów poprawność składniowa programu może stwarzać co najwyżej iluzję jego poprawnego funkcjonowa- nia (o czym wielu z nas zdążyło się już przekonać osobiście). Tym wobec tego, co z punktu widzenia poprawności programów rzeczywiście niezbędne, jest Rygorystyczne testowanie, nie rygorystyczna kontrola typów To właśnie przesądza o tym, że w Pythonie można tworzyć poprawne sys- temy. W C++ większość testów przeprowadzana jest w czasie kompilacji (z nie- licznymi wyjątkami); w języku Java niektóra testy przeprowadzane są w czasie kompilacji, inne zaś (jak kontrola poprawności indeksów tablic) w czasie wy- konania programu. W Pythonie większość testów przeprowadzana jest w czasie wykonania programu, a więc w istocie są one wykonywane, nieważne na ja- kim etapie. W dodatku uruchomienie programu w języku Python odbywa się znacznie szybciej niż uruchomienie podobnego programu w C++, Javie czy C#, a więc samo przeprowadzanie rzeczywistych testów — testów modułów, BRUCE ECKEL 9 9 testów weryfikujących hipotezy, testów sprawdzających alternatywne podej- ścia itp. — odbywa się efektywniej. Napisany w Pythonie program, który zaliczył wszystkie wspomniane testy, może być uważany za tak samo solidny jak jego odpowiednik w C++, Javie czy C#, w dodatku w środowisku Pythona można testy te tworzyć i uruchamiać znacznie szybciej. Robert Martin jest długoletnim członkiem społeczności C++, autorem ksią- żek i artykułów, konsultantem i nauczycielem. No i oczywiście zatwardziałym zwolennikiem statycznego typowania. Tak myślałem o nim do czasu, gdy po lekturze jego blogu7 uświadomiłem sobie, iż to tylko część prawdy: w istocie należy on do programistów „zarażonych testowaniem” i doskonale zdaje sobie sprawę z fragmentarycznego charakteru kontroli wykonywanej przez kompila- tor. A także z tego, ze języki z typowaniem dynamicznym pozwalają ma two- rzenie programów równie solidnych, jak ich statyczne odpowiedniki, jednakże w sposób znacznie bardziej produktywny. Oczywiście Martin także bywał adresatem zwyczajowych komentarzy w ro- dzaju: „Jak możesz myśleć w ten sposób?” — komentarzy, które mnie samego skłoniły do zastanawiania się nad wyższością jednego rodzaju typowania nad drugim. Obydwoje zaczynaliśmy jako zagorzali zwolennicy typowania statycz- nego i interesujące jest to, iż potrzeba doświadczeń na miarę trzęsienia ziemi, by człowiek skłonny był przewartościować swą filozofię. 7 http://www.artima.com/weblogs/viewpost.jsp?thread=4639 — przyp. red.
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Sztuka pisania oprogramowania. Wybór i redakcja Joel Spolsky
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: