Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00127 004609 13086049 na godz. na dobę w sumie
Kod doskonały. Jak tworzyć oprogramowanie pozbawione błędów. Wydanie II - ebook/pdf
Kod doskonały. Jak tworzyć oprogramowanie pozbawione błędów. Wydanie II - ebook/pdf
Autor: Liczba stron: 944
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-3490-8 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> techniki programowania
Porównaj ceny (książka, ebook (-40%), audiobook).

Kultowy podręcznik tworzenia doskonałego oprogramowania!

Steve McConnell wie więcej o budowie oprogramowania niż ktokolwiek inny; mamy ogromne szczęście, że zdecydował się podzielić swoim doświadczeniem oraz wiedzą w tej ważnej i oryginalnej książce.

Alan Cooper, „ojciec” języka Visual Basic, autor książki About Face

Zapewne każdy zgodzi się ze stwierdzeniem, że jeśli jakiś proces odpowiada za nawet 70% błędów w gotowym produkcie, z pewnością wymaga znaczącego usprawnienia... Czy masz jednak świadomość, że właśnie tyle problemów generuje samo wytwarzanie oprogramowania? Te błędy powodują nie tylko usterki w już gotowych programach, niespełniających oczekiwań klientów — odpowiadają także za znaczne opóźnienia przy realizacji zleconych projektów i nagminne przekraczanie zaplanowanego budżetu. Każdy ambitny programista staje zatem przed koniecznością zdobycia wiedzy o takich metodach pracy, które pozwolą szybciej i efektywniej realizować projekty, a przy tym zapewniać najwyższą jakość tworzonego kodu. W końcu na podstawie tych właśnie umiejętności oceniana jest także wartość danego programisty w zespole.

Z tych właśnie powodów niniejsza książka, będąca przejrzystą kompilacją najlepszych technik programowania, zdobyła tak wielkie uznanie w środowisku zawodowców i studentów, osiągając miano podręcznika kultowego. Przed Tobą drugie, zaktualizowane wydanie słynnej publikacji, w której Steve McConnell przedstawia wszystkie aspekty budowy programów, takie jak jakość czy podejście do procesu wytwarzania. Autor rozwija tu tak istotne zagadnienia, jak przebieg budowy klasy, techniki pracy z danymi i strukturami sterującymi, debugowanie, refaktoryzowanie oraz metody i strategie optymalizacji. Znajdziesz tu dziesiątki list kontrolnych, pomocnych w ocenianiu architektury, jakości klas i procedur, nazw zmiennych czy struktur sterujących, a także ponad 500 przykładów dobrego i złego kodu. Dowiesz się, co było przyczyną wielu typowych problemów w przeszłości i jak ich dzisiaj unikać. Opisane metody pracy pomogą utrzymać kontrolę nad dużymi projektami oraz efektywnie rozwijać i modyfikować oprogramowanie w odpowiedzi na zmiany wymagań. Co ważne, można je skutecznie wykorzystywać niezależnie od stosowanego języka programowania!

Dzięki tej książce nauczysz się skutecznie:

Posiądź kluczowe umiejętności tworzenia najwyższej jakości oprogramowania!


Steve McConnell jest jednym z najbardziej szanowanych ekspertów w świecie informatyki. Jest głównym programistą w firmie Construx Software oraz znanym autorem bestsellerowych książek, m.in. Kod doskonały i Rapid Development — obie zostały uhonorowane nagrodą Jolt magazynu „Software Development”, przyznawaną co roku najlepszej książce poświęconej procesowi wytwarzania oprogramowania. Brał udział w projektach realizowanych dla takich firm, jak Microsoft czy Boeing, a w 1998 roku czytelnicy magazynu „Software Development” uznali go obok Billa Gatesa i Linusa Torvaldsa za jedną z trzech najbardziej wpływowych osób w branży oprogramowania.
Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Code Complete Tłumaczenie: Paweł Koronkiewicz ISBN: 978-83-283-3489-2 © 2010, 2017 Helion SA Authorized translation of the English edition of Code Complete, Second Edition © 2004, Steven C. McConnell. This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls of all rights to publish and sell the same. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/koddov Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis treści Wstęp ........................................................................................................................................15 Podziękowania ......................................................................................................................23 Listy kontrolne .......................................................................................................................25 Tabele .......................................................................................................................................27 Rysunki .....................................................................................................................................29 Część I Proces budowy oprogramowania .............................................. 35 Budowa oprogramowania .............................................................................. 37 1. 1.1. Czym jest budowa oprogramowania .....................................................................37 1.2. Znaczenie procesu budowy oprogramowania ...................................................40 1.3. Jak korzystać z tej książki ............................................................................................41 2. Metafory procesu programowania ............................................................. 43 3. 4. 2.1. Znaczenie metafor ........................................................................................................43 2.2. Jak korzystać z metafor w programowaniu .........................................................46 2.3. Popularne metafory programowania ....................................................................47 Przed programowaniem — przygotowania ............................................ 57 3.1. Przygotowania i ich znaczenie .................................................................................58 3.2. Określanie rodzaju budowanego oprogramowania .........................................65 3.3. Definicja problemu .......................................................................................................70 3.4. Określenie wymagań ...................................................................................................72 3.5. Architektura ....................................................................................................................77 3.6. Ilość czasu poświęcanego na przygotowania .....................................................89 Kluczowe decyzje konstrukcyjne ................................................................. 95 4.1. Wybór języka programowania .................................................................................95 4.2. Konwencje programowania ................................................................................... 100 4.3. Twoje położenie na fali technologii .................................................................... 101 4.4. Wybór podstawowych praktyk programowania ............................................ 103 Część II Pisanie dobrego kodu ............................................................... 107 Projektowanie ...................................................................................................109 5. 5.1. Podstawowe problemy projektowania .............................................................. 110 5.2. Podstawowe pojęcia projektowania ................................................................... 113 5.3. Heurystyki — narzędzia projektanta ................................................................... 122 5.4. Techniki projektowania ........................................................................................... 146 5.5. Uwagi o popularnych metodykach pracy ......................................................... 155 Poleć książkęKup książkę 8 Spis treści 6. Klasy z klasą ........................................................................................................161 6.1. Abstrakcyjne typy danych ...................................................................................... 162 6.2. Dobry interfejs klasy ................................................................................................. 169 6.3. Problemy projektowania i implementacji ......................................................... 179 6.4. Przesłanki dla utworzenia klasy ............................................................................. 188 6.5. Specyfika języka ......................................................................................................... 192 6.6. Pakiety klas ................................................................................................................... 192 Procedury wysokiej jakości ..........................................................................197 7.1. Przesłanki utworzenia procedury ......................................................................... 200 7.2. Projektowanie na poziomie procedur ................................................................ 204 7.3. Dobra nazwa procedury .......................................................................................... 207 7.4. Jak długa może być procedura? ........................................................................... 209 7.5. Jak używać parametrów procedur ....................................................................... 211 7.6. Używanie funkcji ........................................................................................................ 217 7.7. Makra i procedury inline .......................................................................................... 218 Programowanie defensywne ......................................................................223 8.1. Zabezpieczanie programu przed niewłaściwymi danymi wejściowymi ........ 224 8.2. Asercje ........................................................................................................................... 225 8.3. Mechanizmy obsługi błędów ................................................................................. 230 8.4. Wyjątki ........................................................................................................................... 234 8.5. Ograniczanie zasięgu szkód powodowanych przez błędy .......................... 239 8.6. Kod wspomagający debugowanie ...................................................................... 241 8.7. Ilość kodu defensywnego w wersji finalnej ...................................................... 245 8.8. Defensywne podejście do programowania defensywnego ....................... 246 Proces Programowania w Pseudokodzie ................................................251 9.1. Budowanie klas i procedur krok po kroku ......................................................... 251 9.2. Pseudokod dla zaawansowanych ........................................................................ 253 9.3. Budowanie procedur metodą PPP ....................................................................... 256 9.4. Alternatywy dla pseudokodu ................................................................................ 269 7. 8. 9. Część III Zmienne ..................................................................................... 273 Zmienne w programie ...................................................................................275 10. 10.1. Podstawowa wiedza o danych ........................................................................... 276 10.2. Deklarowanie zmiennych ..................................................................................... 277 10.3. Inicjalizowanie zmiennych ................................................................................... 278 10.4. Zakres .......................................................................................................................... 282 10.5. Trwałość ...................................................................................................................... 289 10.6. Czas wiązania ............................................................................................................ 290 Poleć książkęKup książkę 10.7. Związek między typami danych i strukturami sterowania ........................ 292 10.8. Jedno przeznaczenie każdej zmiennej ............................................................. 293 Potęga nazwy zmiennej ................................................................................297 11. Spis treści 9 11.1. Wybieranie dobrej nazwy ..................................................................................... 297 11.2. Nazwy a rodzaje danych ....................................................................................... 303 11.3. Potęga konwencji nazw ........................................................................................ 308 11.4. Nieformalne konwencje nazw ............................................................................ 310 11.5. Standardowe prefiksy ............................................................................................ 317 11.6. Nazwy krótkie a czytelne ...................................................................................... 319 11.7. Nazwy, których należy unikać ............................................................................. 322 Podstawowe typy danych ............................................................................327 12.1. Liczby ........................................................................................................................... 327 12.2. Liczby całkowite ....................................................................................................... 329 12.3. Liczby zmiennoprzecinkowe ............................................................................... 331 12.4. Znaki i ciągi znakowe ............................................................................................. 333 12.5. Zmienne logiczne .................................................................................................... 336 12.6. Typy wyliczeniowe .................................................................................................. 338 12.7. Stałe nazwane ........................................................................................................... 343 12.8. Tablice ......................................................................................................................... 345 12.9. Tworzenie własnych typów (aliasy) ................................................................... 346 Inne typy danych ..............................................................................................355 13.1. Struktury ..................................................................................................................... 355 13.2. Wskaźniki .................................................................................................................... 359 13.3. Dane globalne .......................................................................................................... 371 12. 13. Część IV Instrukcje .................................................................................... 383 14. Struktura kodu liniowego..............................................................................385 14.1. Instrukcje, które wymagają określonej kolejności ........................................ 385 14.2. Instrukcje, których kolejność nie ma znaczenia ............................................ 388 Instrukcje warunkowe ....................................................................................393 15.1. Instrukcje if ................................................................................................................ 393 15.2. Instrukcje case .......................................................................................................... 398 Pętle ......................................................................................................................405 15. 16. 16.1. Wybieranie rodzaju pętli ....................................................................................... 405 16.2. Sterowanie pętlą ...................................................................................................... 410 16.3. Łatwe tworzenie pętli — od wewnątrz ............................................................ 422 16.4. Pętle i tablice ............................................................................................................. 424 Poleć książkęKup książkę 10 Spis treści 17. Nietypowe struktury sterowania ................................................................427 18. 19. 17.1. Wiele wyjść z procedury ........................................................................................ 427 17.2. Rekurencja ................................................................................................................. 429 17.3. Instrukcja goto ......................................................................................................... 434 17.4. Nietypowe struktury sterowania z perspektywy .......................................... 444 Metody oparte na tabelach ..........................................................................449 18.1. Metody oparte na tabelach — wprowadzenie ............................................. 449 18.2. Tabele o dostępie bezpośrednim ...................................................................... 451 18.3. Tabele o dostępie indeksowym .......................................................................... 462 18.4. Tabele o dostępie schodkowym ........................................................................ 464 18.5. Inne metody wyszukiwania w tabelach ........................................................... 467 Ogólne problemy sterowania .....................................................................469 19.1. Wyrażenia logiczne ................................................................................................. 469 19.2. Instrukcje złożone (bloki) ...................................................................................... 480 19.3. Instrukcje puste ........................................................................................................ 481 19.4. Praca z głębokimi zagnieżdżeniami .................................................................. 482 19.5. Programowanie strukturalne .............................................................................. 490 19.6. Struktury sterujące i złożoność ........................................................................... 493 Część V Sprawna praca z kodem ............................................................ 497 20. Jakość oprogramowania ...............................................................................499 21. 22. 20.1. Składowe jakości ...................................................................................................... 499 20.2. Metody podwyższania jakości ............................................................................ 502 20.3. Skuteczność metod podwyższania jakości ..................................................... 505 20.4. Kiedy przeprowadzać kontrolę jakości ............................................................ 509 20.5. Ogólna Zasada Jakości Oprogramowania ...................................................... 509 Programowanie zespołowe .........................................................................513 21.1. Przegląd metod programowania zespołowego ........................................... 514 21.2. Programowanie w parach .................................................................................... 517 21.3. Formalne inspekcje ................................................................................................. 519 21.4. Inne metody programowania zespołowego ................................................. 526 Testowanie .........................................................................................................533 22.1. Rola testów programisty ....................................................................................... 534 22.2. Zalecane podejście do testów programisty ................................................... 537 22.3. Praktyczne techniki testowania .......................................................................... 539 22.4. Typowe błędy ........................................................................................................... 550 22.5. Narzędzia wspomagające testowanie .............................................................. 556 Poleć książkęKup książkę Spis treści 11 22.6. Usprawnianie testów ............................................................................................. 561 22.7. Gromadzenie informacji o testach .................................................................... 563 Debugowanie ....................................................................................................569 23. 23.1. Wprowadzenie ......................................................................................................... 569 23.2. Wyszukiwanie defektu ........................................................................................... 574 23.3. Usuwanie defektu ................................................................................................... 585 23.4. Debugowanie a psychologia ............................................................................... 588 23.5. Narzędzia debugowania — oczywiste i mniej oczywiste .......................... 591 Refaktoryzacja ...................................................................................................597 24.1. Ewolucja oprogramowania i jej odmiany ........................................................ 598 24.2. Refaktoryzacje — wprowadzenie ...................................................................... 599 24.3. Wybrane refaktoryzacje ........................................................................................ 605 24.4. Bezpieczne przekształcanie kodu ...................................................................... 613 24.5. Strategie refaktoryzacji .......................................................................................... 615 Strategie optymalizacji kodu .......................................................................621 24. 25. 25.1. Wydajność kodu ...................................................................................................... 622 25.2. Optymalizowanie kodu ......................................................................................... 625 25.3. Rodzaje otyłości i lenistwa ................................................................................... 632 25.4. Pomiary ....................................................................................................................... 637 25.5. Iterowanie .................................................................................................................. 639 25.6. Strategie optymalizacji kodu — podsumowanie ......................................... 640 26. Metody optymalizacji kodu .........................................................................645 26.1. Struktury logiczne ................................................................................................... 646 26.2. Pętle ............................................................................................................................. 651 26.3. Przekształcenia danych ......................................................................................... 660 26.4. Wyrażenia ................................................................................................................... 665 26.5. Procedury ................................................................................................................... 674 26.6. Reimplementacja w języku niskiego poziomu .............................................. 675 26.7. Im bardziej świat się zmienia, tym więcej zostaje bez zmian ................... 677 Część VI Środowisko programowania .................................................... 681 Jak rozmiar programu wpływa na jego budowę .................................683 27. 27.1. Wielkość projektu a komunikacja ...................................................................... 684 27.2. Skala rozmiarów projektów ................................................................................. 684 27.3. Wpływ wielkości projektu na liczbę błędów .................................................. 685 27.4. Wpływ wielkości projektu na efektywność pracy ......................................... 687 27.5. Wpływ wielkości projektu na wykonywaną pracę ....................................... 687 Poleć książkęKup książkę 12 Spis treści 28. Zarządzanie w programowaniu .................................................................695 29. 30. 28.1. Zachęcanie do budowy dobrego kodu ........................................................... 696 28.2. Zarządzanie konfiguracją ...................................................................................... 698 28.3. Budowanie harmonogramu ................................................................................ 705 28.4. Pomiary ....................................................................................................................... 712 28.5. Ludzkie traktowanie programistów .................................................................. 715 28.6. Współpraca z przełożonymi ................................................................................. 721 Integracja ............................................................................................................725 29.1. Znaczenie metod integracji ................................................................................. 725 29.2. Częstość integracji — końcowa czy przyrostowa? ....................................... 727 29.3. Przyrostowe strategie integracji ......................................................................... 730 29.4. Codzienna kompilacja i test dymowy ............................................................... 738 Narzędzia programowania ...........................................................................747 30.1. Narzędzia do projektowania ................................................................................ 748 30.2. Narzędzia do pracy z kodem źródłowym ........................................................ 748 30.3. Narzędzia do pracy z kodem wykonywalnym ............................................... 754 30.4. Środowiska narzędzi programowania .............................................................. 758 30.5. Budowanie własnych narzędzi ........................................................................... 759 30.6. Narzędzia przyszłości ............................................................................................. 761 Część VII Rzemiosło programisty ............................................................. 765 Układ i styl ...........................................................................................................767 31. 31.1. Wprowadzenie ......................................................................................................... 768 31.2. Techniki formatowania .......................................................................................... 774 31.3. Style formatowania ................................................................................................. 776 31.4. Formatowanie struktur sterujących .................................................................. 782 31.5. Formatowanie instrukcji ....................................................................................... 789 31.6. Formatowanie komentarzy .................................................................................. 800 31.7. Formatowanie procedur ....................................................................................... 802 31.8. Formatowanie klas .................................................................................................. 804 Kod, który opisuje się sam ............................................................................813 32.1. Zewnętrzna dokumentacja programu ............................................................. 813 32.2. Styl programowania jako dokumentacja ........................................................ 814 32.3. Komentować czy nie komentować ................................................................... 817 32.4. Zasady pisania dobrych komentarzy ................................................................ 821 32.5. Metody pisania komentarzy ................................................................................ 828 32.6. Normy IEEE ................................................................................................................. 849 32. Poleć książkęKup książkę 33. Cechy charakteru .............................................................................................855 Spis treści 13 33.1. Czy osobowość jest bez znaczenia? .................................................................. 856 33.2. Inteligencja i skromność ....................................................................................... 857 33.3. Ciekawość .................................................................................................................. 858 33.4. Uczciwość intelektualna ........................................................................................ 862 33.5. Komunikacja i współpraca .................................................................................... 865 33.6. Kreatywność i dyscyplina ...................................................................................... 865 33.7. Lenistwo ..................................................................................................................... 866 33.8. Cechy, które znaczą mniej, niż myślisz ............................................................. 867 33.9. Nawyki ......................................................................................................................... 869 Powracające wątki — przegląd ..................................................................873 34.1. Walka ze złożonością .............................................................................................. 873 34.2. Wybierz swój proces ............................................................................................... 875 34.3. Pisz programy dla ludzi, nie tylko dla komputerów .................................... 877 34.4. Programuj do języka, a nie w nim ...................................................................... 879 34.5. Konwencje jako pomoc w koncentracji uwagi .............................................. 880 34.6. Programowanie w kategoriach dziedziny problemu .................................. 881 34.7. Uwaga, spadające odłamki! ................................................................................. 884 34.8. Iteruj, iteruj i jeszcze raz iteruj ............................................................................. 886 34.9. Nie będziesz łączył religii z programowaniem .............................................. 887 Gdzie znaleźć więcej informacji .................................................................891 35.1. Programowanie ....................................................................................................... 892 35.2. Szersze spojrzenie na budowę oprogramowania ........................................ 893 35.3. Periodyki ..................................................................................................................... 895 35.4. Plan czytelniczy programisty ............................................................................... 896 35.5. Stowarzyszenia zawodowe .................................................................................. 898 Bibliografia .........................................................................................................899 Skorowidz ...........................................................................................................918 34. 35. Poleć książkęKup książkę 14 Spis treści Poleć książkęKup książkę cc2e.com/0861 Rozdział 8. Programowanie defensywne W tym rozdziale „ 8.1. Zabezpieczanie programu przed niewłaściwymi danymi wejściowymi — strona 224 „ 8.2. Asercje — strona 225 „ 8.3. Mechanizmy obsługi błędów — strona 230 „ 8.4. Wyjątki — strona 234 „ 8.5. Ograniczanie zasięgu szkód powodowanych przez błędy — strona 239 „ 8.6. Kod wspomagający debugowanie — strona 241 „ 8.7. Ilość kodu defensywnego w wersji finalnej — strona 245 „ 8.8. Defensywne podejście do programowania defensywnego — strona 246 Podobne tematy „ Ukrywanie informacji: „Ukrywaj tajemnice (ukrywanie informacji)” w pod- rozdziale 5.3 „ Przygotowywanie projektu na przyszłe zmiany: „Identyfikuj obszary poten- cjalnych zmian” w podrozdziale 5.3 „ Architektura: podrozdział 3.5 „ Projektowanie: rozdział 5. „ Debugowanie: rozdział 23. Programowanie defensywne nie oznacza przyjmowania postawy obronnej w trakcie omawiania kodu („To przecież doskonale działa!”). Jest to nawiązanie do defensywnej jazdy samochodem, która sprowadza się do przyjęcia założe- nia, że nigdy nie można być pewnym zachowania innych kierowców. Dzięki temu założeniu, gdy na drodze wydarzy się coś niebezpiecznego, defensywny kierowca ma duże szanse wyjść z przygody bez szwanku. Bez względu na to, komu zostanie przypisana wina za spowodowanie zagrożenia, na każdym spo- czywa odpowiedzialność za chronienie swojego zdrowia i życia. W programowa- niu defensywnym podstawowym celem jest to, by przekazanie do procedury złych danych nie powodowało żadnych szkód, nawet jeżeli winę za doprowa- dzenie do takiej sytuacji będzie ponosiła inna część programu. Bardziej ogólnie, punktem wyjścia do programowania defensywnego jest przyznanie, że program będzie zmieniany i będą pojawiać się problemy. Dobry programista bierze to w trakcie pisania kodu pod uwagę. Poleć książkęKup książkę 224 Rozdział 8. Programowanie defensywne W tym rozdziale piszę o tym, jak chronić się przed zimnym, okrutnym światem błędnych danych, zdarzeń, które „nigdy” nie nastąpią, i błędów innych progra- mistów. Jeżeli masz duże doświadczenie, możesz pominąć pierwszy podroz- dział — poświęcony zabezpieczaniu programu przed wadliwymi danymi wej- ściowymi — i przejść od razu do podrozdziału 8.2, w którym omawiane jest zagadnienie właściwego stosowania asercji. 8.1. Zabezpieczanie programu przed niewłaściwymi danymi wejściowymi Być może spotkałeś się w szkole ze sformułowaniem „garbage in, garbage out” (śmieci na wejściu, śmieci na wyjściu). Jest to zasadniczo przeniesiona na grunt programowania zasada caveat emptor1: użytkownicy, strzeżcie się! W przypadku oprogramowania, które ma być wdrażane i aktywnie wykorzysty- wane, zasada „garbage in, garbage out” nie jest wystarczająca. Dobry program nigdy nie wyprowadza na wyjście śmieci, bez względu na przekazane mu dane. Może on działać zgodnie z zasadą „śmieci na wejściu, nic na wyjściu”, „śmieci na wejściu, komunikat błędu na wyjściu” lub „śmieci nie są dozwolone”. Według współczesnych standardów działanie na zasadzie „śmieci na wejściu, śmieci na wyjściu” znamionuje niedopracowaną i niebezpieczną w użyciu aplikację. Można wyróżnić trzy techniki radzenia sobie ze śmieciami na wejściu pro- gramu: Sprawdzanie wartości wszystkich danych ze źródeł zewnętrznych. Przy pobie- raniu danych z pliku, od użytkownika, z sieci lub za pośrednictwem jakiego- kolwiek innego interfejsu zewnętrznego należy sprawdzić, czy mieszczą się one w dopuszczalnym zakresie. W przypadku wartości liczbowej ważne jest, aby dane były liczbą i aby mieściła się ona w określonym przedziale. W przypadku ciągów znakowych problemem może być ich długość. Jeżeli ciąg ma reprezento- wać pewien szczególny zakres wartości (na przykład identyfikator transakcji lub klienta), należy dołożyć wszelkich starań, aby zweryfikować użyteczność odczytanych danych. Mając do czynienia z aplikacjami wymagającymi zabez- pieczeń, warto zwrócić szczególną uwagę na niepożądane dane, które mogą posłużyć do zaatakowania systemu: celowe przepełnienia bufora, „wstrzyknię- cia” SQL, HTML lub XML, błędy przepełnienia liczb całkowitych, dane prze- kazywane do wywołań systemowych itp. Sprawdzanie wartości wszystkich parametrów wejściowych procedury. Zasada sprawdzania wartości parametrów wejściowych procedur jest powtórzeniem zasady weryfikowania danych pobieranych ze źródeł zewnętrznych, z tą róż- nicą, że miejsce interfejsu zewnętrznego zajmuje interfejs procedury. W podroz- dziale 8.5, „Ograniczanie zasięgu szkód powodowanych przez błędy”, przedsta- wię praktyczną metodę określania, które z procedur wymagają sprawdzania wartości wejściowych. 1 Klauzula handlowa nakazująca kupującemu sprawdzić towar przy zakupie i zwalniająca sprzedawcę z odpo- wiedzialności — przyp. tłum. Poleć książkęKup książkę 8.2. Asercje 225 Określanie zasad obsługi złych danych. Co robisz po wykryciu błędnego para- metru? W zależności od projektu możesz zdecydować się na jeden z kilkunastu schematów działania, które opisuję szczegółowo w podrozdziale 8.3 „Mecha- nizmy obsługi błędów”. Programowanie defensywne jest świetnym uzupełnieniem innych opisywanych w tej książce technik podnoszenia jakości kodu, a jego najlepszą formą jest unikanie błędów od pierwszej wersji programu. Iteracyjne projektowanie, pisa- nie pseudokodu przed rozpoczęciem pracy z właściwym kodem, pisanie testów przed rozpoczęciem pisania kodu i niskopoziomowe inspekcje konstrukcyjne to działania, które pomagają unikać wprowadzania błędów — warto poświęcić im więcej uwagi niż samej idei programowania defensywnego. Jest ono jednak koncepcją, którą można bez przeszkód łączyć z każdą inną metodą pracy. Jak ilustruje to rysunek 8.1, zabezpieczanie się przed pozornie drobnymi pro- blemami może być w rzeczywistości istotniejsze, niż się początkowo wydaje. W dalszej części tego rozdziału opiszę różne techniki sprawdzania danych ze źródeł wewnętrznych, weryfikowania parametrów wejściowych i obsługi niepo- prawnych wartości. Rysunek 8.1. Część pływającego mostu na drodze I-90 w Seattle zatonęła w trakcie burzy, ponieważ nie zamknięto pływaków, które utrzymywały go na powierzchni wody. Deszcz zalał je i most stał się zbyt ciężki. W trakcie budowy oprogramowania zabezpieczanie się przed drobiazgami ma większe znacznie niż się wydaje. 8.2. Asercje Asercja to kod stosowany w trakcie pracy nad oprogramowaniem — zazwyczaj procedura lub makro — który działa jako mechanizm automatycznej kontroli działania programu w trakcie jego wykonywania. Jeżeli asercja jest spełniona (ma wartość „prawda”), to znaczy, że kod działa zgodnie z oczekiwaniami. Jeżeli Poleć książkęKup książkę 226 Rozdział 8. Programowanie defensywne nie jest spełniona, oznacza to wystąpienie nieoczekiwanego błędu. Na przykład gdy działanie systemu opiera się na założeniu, że plik z informacjami o klien- tach nigdy nie będzie przechowywał więcej niż 50 tysięcy rekordów, program może zawierać asercję mówiącą, że ich liczba jest mniejsza lub równa 50 tysięcy. Dopóki warunek ten będzie spełniony, asercja nie będzie wpływać na działanie kodu. Gdy jednak okaże się, że plik zawiera więcej niż 50 tysięcy rekordów, przy- pomni ona o swoim istnieniu zgłoszeniem wystąpienia błędu. Asercje są szczególnie praktyczne w przypadku programów dużych i skompli- kowanych oraz takich, które wymagają wysokiego poziomu niezawodności. Umożliwiają wtedy szybkie wykrywanie nietrafionych założeń interfejsu, nowych błędów pojawiających się przy wprowadzaniu modyfikacji itp. Asercja wymaga zazwyczaj dwóch argumentów: wyrażenia logicznego opisu- jącego założenie, które powinno być prawdziwe, oraz komunikatu, który będzie wyświetlany, w przypadku gdy wyrażenie logiczne będzie miało wartość „fałsz”. Oto przykład asercji w języku Java, która sprawdza, czy wartość zmiennej denominator (mianownik) jest różna od zera: Przykład asercji (Java) assert denominator != 0 : Mianownik ma nieoczekiwanÈ wartoĂÊ 0. ; Asercja ta bada założenie, że wartość denominator jest różna od 0. Pierwszy argu- ment, denominator != 0, to wyrażenie logiczne, czyli o wartości „prawda” lub „fałsz”. Drugi to komunikat, który zostanie wypisany, gdy pierwszy argument będzie miał wartość „fałsz”. Warto używać asercji do opisywania założeń przyjętych przy pisaniu kodu i wy- krywania nieoczekiwanych sytuacji. Oto warunki, które mogą one sprawdzać: „ wartość parametru wejściowego (lub wyjściowego) mieści się w oczekiwa- nym zakresie; „ plik lub strumień jest otwarty (lub zamknięty), gdy procedura rozpoczyna pracę (lub gdy kończy pracę); „ wskaźnik pliku lub strumienia jest na jego początku (lub końcu), gdy pro- cedura rozpoczyna pracę (lub gdy kończy pracę); „ plik lub strumień jest otwarty w trybie tylko-do-odczytu, tylko-do-zapisu lub trybie odczytywania i zapisywania; „ wartość zmiennej wejściowej nie została zmieniona w procedurze; „ wskaźnik nie jest wskaźnikiem pustym; „ tablica lub inny obiekt kontenerowy przekazany do procedury ma pojem- ność co najmniej X elementów danych; „ tablica została zainicjalizowana prawdziwymi danymi; „ obiekt kontenerowy jest pusty (lub pełny), gdy procedura rozpoczyna pracę (lub kończy pracę); „ wyniki wysoce zoptymalizowanej, złożonej procedury są zgodne z wyni- kami procedury wolniejszej, ale bardziej przejrzystej i lepiej sprawdzonej. Poleć książkęKup książkę 8.2. Asercje 227 Oczywiście to tylko najprostsze przykłady — procedury mogą opierać swoje działanie na dużo bardziej szczegółowych założeniach. Można je opisywać mię- dzy innymi za pomocą asercji. W typowej sytuacji wyświetlanie komunikatów asercji przez kod przekazywany użytkownikom nie jest pożądane. Są one narzędziem przeznaczonym do stoso- wania tylko podczas pisania i modyfikowania kodu, normalnie są więc kom- pilowane podczas pracy z programem i pomijane w kompilacji wersji finalnej. W trakcie pracy z programem asercje zwracają uwagę na sprzeczne założenia, nieoczekiwane sytuacje, złe wartości przekazywane procedurom i inne podobne problemy. Pominięcie ich w kompilacji końcowej pozwala uniknąć ich nieko- rzystnego wpływu na wydajność. Budowanie własnego mechanizmu asercji Patrz też: Budowanie własnej procedury asercyjnej to dobry przykład programowania do języka (zamiast tylko w języku). Więcej na ten temat w podrozdziale 34.4 „Programuj do języka, a nie w nim”. Wiele języków standardowo zapewnia możliwość korzystania z asercji — należą do nich C++, Java i Microsoft Visual Basic. Jeżeli stosowany język nie został wyposażony w procedury asercyjne, łatwo napisać je samodzielnie. Przykładowo, standardowe makro C++ assert nie daje możliwości korzystania z komunika- tów tekstowych. Oto ulepszona wersja procedury ASSERT, również zaimplemen- towana jako makro C++: Przykładowe makro asercji (C++) #define ASSERT( condition, message ) { \ if ( !(condition) ) { \ LogError( BïÈd asercji: , \ #condition, message ); \ exit( EXIT_FAILURE ); \ } \ } Stosowanie asercji Oto porady dotyczące korzystania z asercji: Dla zdarzeń, których wystąpienia oczekujesz, stosuj kod obsługujący błędy; używaj asercji tylko dla tych sytuacji, które nigdy nie powinny mieć miejsca. Asercje sprawdzają, czy wystąpiła sytuacja, która nigdy nie powinna się zdarzyć. Kod obsługi błędów wykrywa natomiast wszystkie nietypowe okoliczności i zapewnia odpowiednie dla nich przetwarzanie. Nie muszą one zdarzać się czę- sto, ale zostały przewidziane przez programistę i kod przekazywany użyt- kownikowi musi zapewniać ich obsługę. Kod ten odpowiada za kontrolę danych wejściowych, podczas gdy zadaniem asercji jest wykrywanie błędów w programie. Kod obsługujący błędy radzi sobie z nietypową sytuacją, pozwalając progra- mowi zareagować w możliwie niekłopotliwy sposób. Jeżeli nietypowe oko- liczności powodują uaktywnienie asercji, niekłopotliwa reakcja nie jest odpo- wiedzią — w tym przypadku wymagana jest zmiana kodu źródłowego, rekompilacja i udostępnienie nowej wersji oprogramowania. Poleć książkęKup książkę 228 Rozdział 8. Programowanie defensywne Patrz też: Przedstawione tu zagadnienie można także rozpatrywać jako przykład jednego z licznych problemów związanych z umieszczaniem wielu instrukcji w jednym wierszu. Więcej takich przykładów można znaleźć w punkcie „Nie więcej niż jedna instrukcja w wierszu” w podrozdziale 31.5. Patrz też: O warunkach wstępnych i końcowych można przeczytać w książce Programowanie zorientowane obiektowo (Meyer 2005). Dobrym podejściem do asercji jest traktowanie ich jako „wykonywalnej doku- mentacji” — nie sprawią one, że kod będzie działał, ale mogą być aktywną formą opisu zastępującą lub uzupełniającą komentarze. Unikaj kodu wykonywalnego w asercjach. Umieszczenie w asercji kodu może skutkować tym, że zostanie on wyeliminowany z programu przy wyłączaniu jej mechanizmu. Przypuśćmy, że w programie znajduje się asercja: Niebezpieczna forma asercji (Visual Basic) Debug.Assert( PerformAction() ) Nie moĪna wykonaü operacji Problem polega tu na tym, że gdy asercje nie zostaną skompilowane, nie zosta- nie skompilowany również kod wykonujący operację, czyli wywołanie Perform ´Action(). Tego typu instrukcje należy zawsze umieszczać w odrębnych wier- szach — ich wynik można zapisać w zmiennej stanu i to jej wartość powinna podlegać badaniu. Oto przykład bezpiecznego użycia asercji: Bezpieczne użycie asercji (Visual Basic) actionPerformed = PerformAction() Debug.Assert( actionPerformed ) Nie moĪna wykonaü operacji Używaj asercji do opisywania i weryfikowania warunków wstępnych i końco- wych. Warunki wstępne i końcowe są elementem podejścia do projektowania i programowania znanego jako „projektowanie kontraktowe” (Meyer 2005). Gdy zostają one określone, procedura lub klasa zawiera rodzaj umowy z innymi częściami programu. Warunki wstępne to charakterystyki, których przygotowanie kod wywołujący musi zapewnić, zanim wywoła procedurę lub utworzy obiekt. Są one zobowią- zaniami kodu klienckiego wobec kodu wywoływanego. Warunki końcowe to charakterystyki, które procedura lub klasa „obiecuje” osią- gnąć w chwili zakończenia swojej pracy. Są to zobowiązania procedury lub klasy wobec kodu wywołującego. Asercje to dobre narzędzie do dokumentowania warunków wstępnych i końco- wych. Warunki te mogą być też opisywane w komentarzach, jednak asercje mają tę przewagę, że dynamicznie sprawdzają, czy są one spełnione. W poniższym przykładzie asercje zostały użyte do opisania warunków wstęp- nych i końcowych procedury Velocity. Przykład wykorzystania asercji do opisu warunków wstępnych i końcowych (Visual Basic) Private Function Velocity ( _ szybkoĞü ByVal latitude As Single, _ szerokoĞü geograficzna ByVal longitude As Single, _ dáugoĞü geograficzna ByVal elevation As Single _ wysokoĞü ) As Single warunki wstĊpne Debug.Assert ( -90 = latitude And latitude = 90 ) Debug.Assert ( 0 = longitude And longitude 360 ) Poleć książkęKup książkę Patrz też: Więcej o niezawodności w punkcie „Poprawność a odporność” w podrozdziale 8.3. 8.2. Asercje 229 Debug.Assert ( -500 = elevation And elevation = 75000 ) ... warunki koĔcowe Debug.Assert ( 0 = returnVelocity And returnVelocity = 600 ) zwracana wartoĞü Velocity = returnVelocity End Function Gdyby wartości latitude, longitude i elevation były pobierane z zewnątrz, wykrywanie w nich nieprawidłowości i odpowiednie reakcje programu powi- nien zapewniać kod obsługi błędów, a nie asercje. W przypadku gdy dane pochodzą z zaufanego, wewnętrznego źródła, a konstrukcja procedury bazuje na założeniu, że wartości będą mieściły się w dopuszczalnych zakresach, użycie asercji jest właściwe. Aby zapewnić wysoką niezawodność, używaj asercji, a potem zapewniaj obsługę błędów. W przypadku wystąpienia błędu procedura może użyć asercji lub kodu do jego obsługi, ale nie może skorzystać z obu tych mechanizmów jednocześnie. Niektórzy eksperci twierdzą nawet, że stosowanie tylko jednego z nich jest w zupełności wystarczające (Meyer 2005). Spotykane w codziennym życiu programy i projekty nie są jednak zazwyczaj na tyle uporządkowane, aby można było ograniczyć się do samych tylko asercji. W dużych, rozwijanych latami systemach różne części mogą być projektowane przez różne osoby na przestrzeni 5 lub 10 lat, a czasem nawet dłuższego okresu. Projektantów dzieli wtedy czas i wiele ewoluujących wersji kodu. Często są oni ukierunkowani na stosowanie zupełnie innych technologii. Dodatkowo, jeżeli część systemu pochodzi ze źródeł zewnętrznych, pojawia się separacja geograficzna. W różnych fazach czasu życia systemu programiści stosują odmienne konwencje pisania kodu. Ponadto w dużych zespołach zawsze poja- wią się programiści bardziej i mniej sumienni, więc różne części kodu są bar- dziej lub mniej rygorystycznie przeglądane. Niektórzy dbają o dokładniejsze testy jednostkowe, natomiast gdy zespoły testujące pracują w różnych stronach świata i podlegają presji natury ekonomicznej, wynikiem jest niejednolita w poszczególnych wersjach jakość przeprowadzanych testów. Nie można też liczyć na pełne testowanie regresyjne na poziomie systemu. W takich warunkach za wykrywanie tego samego błędu może odpowiadać zarówno asercja, jak i kod obsługi błędów. W kodzie źródłowym programu Microsoft Word warunki, które powinny być zawsze spełnione, są opisane aser- cjami, ale istnieje dla nich także kod obsługujący błędy, który może zostać uru- chomiony, gdyby asercja zawiodła. W wyjątkowo dużych, złożonych i długo rozwijanych aplikacjach takich jak Word asercje, jako narzędzie pozwalające wykrywać jak największą liczbę błędów programistycznych, są bardzo pomocne. Przy takim stopniu złożoności aplikacji (miliony wierszy kodu) i po tylu gene- racjach zmian trudno jednak realistycznie oczekiwać, że każdy możliwy błąd zostanie wykryty i poprawiony przed oddaniem wersji przeznaczonej dla użyt- kowników. Stąd potrzeba zapewnienia obsługi nieoczekiwanych nieprawidło- wości także w tej wersji programu. Poleć książkęKup książkę 230 Rozdział 8. Programowanie defensywne Oto przykład takiej konstrukcji dla funkcji Velocity: Przykład wykorzystania asercji do opisu warunków wstępnych i końcowych (Visual Basic) Private Function Velocity ( _ szybkoĞü ByVal latitude As Single, _ szerokoĞü geograficzna ByVal longitude As Single, _ dáugoĞü geograficzna ByVal elevation As Single _ wysokoĞü ) As Single Kod asercji. warunki wstĊpne Debug.Assert ( -90 = latitude And latitude = 90 ) Debug.Assert ( 0 = longitude And longitude 360 ) Debug.Assert ( -500 = elevation And elevation = 75000 ) ... Kod, który obsługuje złe dane wejściowe w czasie wykonywania. Oczyszczanie danych wejĞciowych. WartoĞci powinny mieĞciü siĊ w zakresach opisanych przez asercje, ale gdy tak nie jest, zostają zmienione na najbliĪszą dopuszczalną wartoĞü. If ( latitude -90 ) Then latitude = -90 ElseIf ( latitude 90 ) Then latitude = 90 End If If ( longitude 0 ) Then longitude = 0 ElseIf ( longitude 360 ) Then ... 8.3. Mechanizmy obsługi błędów Asercje mają zapewniać obsługę błędów, które nigdy nie powinny wystąpić. Co należy robić z błędami, które są oczekiwane? W zależności od sytuacji można zwracać wartość neutralną, podstawiać następny element poprawnych danych, powtarzać wcześniejszą odpowiedź, wstawiać najbliższą wartość w dopuszczal- nym zakresie, rejestrować ostrzeżenie w pliku, zwracać kod błędu, wywoły- wać procedurę albo obiekt obsługi błędów, wyświetlać komunikat lub prze- rywać pracę programu. Można też stosować połączenia powyższych technik. Oto opisy tych podstawowych schematów obsługi błędów: Zwracanie wartości neutralnej. Czasem najlepszą reakcją na złe dane jest kon- tynuowanie pracy i zwrócenie wartości, która nie wywoła żadnych szkód. Obli- czenie numeryczne może zwracać 0. Operacja na ciągach znakowych — ciąg pusty. Operacja wskaźnikowa może zwracać pusty wskaźnik. Procedura rysu- jąca, która otrzymuje niepoprawną wartość opisującą kolor w grze wideo, może używać standardowego koloru tła lub rysunku. Taka sama procedura, która jednak wyświetla dane z prześwietlenia chorego na raka pacjenta, nie powinna zwracać „wartości neutralnej”. W takim przypadku rozwiązaniem lepszym niż wyświetlenie niepoprawnych danych jest przerwanie pracy programu. Podstawianie następnego poprawnego elementu danych. Przy przetwarzaniu strumienia danych najlepszym wyjściem może być kontynuowanie zwracania tych, które są poprawne. Jeżeli przy odczytywaniu rekordów z bazy okaże się, Poleć książkęKup książkę 8.3. Mechanizmy obsługi błędów 231 że jeden z nich jest uszkodzony, rozwiązaniem jest kontynuowanie odczytu aż do znalezienia rekordu poprawnego. Jeżeli sto razy na sekundę odczytujesz tem- peraturę i pojedyncza wartość okazuje się niepoprawna, dopuszczalne może być ograniczenie obsługi błędów do oczekiwania na kolejny odczyt. Powtarzanie wcześniejszej odpowiedzi. Jeżeli program odczytujący tempera- turę nie otrzymuje pojedynczego odczytu, może zwracać ostatnią znaną war- tość — choć zależy to od konkretnego zastosowania, na ogół można liczyć na to, że temperatura nie ulegnie znacznej zmianie w ciągu jednej setnej sekundy. Również w grze wideo, gdy pojawia się żądanie pokrycia części ekranu błędnym kolorem, można pozostać przy kolorze wykorzystywanym wcześniej. Gdy jed- nak masz do czynienia z autoryzowaniem transakcji bankomatu, rozwiązanie polegające na powtórzeniu ostatniej odpowiedzi z oczywistych względów nie jest dopuszczalne. Podstawianie najbliższej dopuszczalnej wartości. W niektórych sytuacjach można zdecydować się na zwracanie najbliższej wartości mieszczącej się w dopuszczalnym zakresie. Tak postąpiliśmy w ostatniej wersji funkcji Velocity. Rozwiązanie to sprawdza się zazwyczaj przy rejestrowaniu odczytów różnych instrumentów. Termometr może być skalibrowany do pracy w zakresie od 0 do 100 stopni. Przy odczycie wartości niższej można podstawiać 0, czyli naj- bliższą wartość w dopuszczalnym przedziale. Podobnie, przy odczycie wartości wyższej od 100 można podstawiać 100. W przypadku operacji na ciągach znako- wych, gdy liczba mająca opisywać ich długość jest mniejsza od 0, można pod- stawiać 0. Mój samochód stosuje takie podejście przy cofaniu. Ponieważ na skali szybkościomierza nie ma wartości ujemnych, pokazuje on wtedy prędkość 0 — najbliższą w dopuszczalnym zakresie. Rejestrowanie ostrzeżenia w pliku. Odpowiedzią na wykrycie błędnych danych może być zapisanie w pliku stosownego ostrzeżenia i kontynuowanie pracy. Podejście takie można łączyć z innymi, na przykład z podstawianiem najbliższej dopuszczalnej wartości lub następnego poprawnego elementu danych. Gdy korzystasz z pewnego rodzaju dziennika (logu), musisz rozważyć, czy może on zostać upubliczniony — niektóre aplikacje mogą zmuszać do użycia szyfrowa- nia lub innego rodzaju ochrony. Zwracanie kodu błędu. Możesz wybrać części systemu, w których będzie imple- mentowana obsługa błędów, i ograniczyć reakcję na nieprawidłowości w innych częściach do zgłaszania ich wystąpienia. Wówczas procedury na pewnym poziomie oczekują, że to procedury stojące wyżej w hierarchii wywołań zapew- nią odpowiednią obsługę błędów. Można wyróżnić kilka sposobów powiada- miania innych elementów systemu o ich wystąpieniu: „ przypisywanie pewnej wartości zmiennej stanu, „ zwracanie wartości opisującej stan jako wartości funkcji, „ zgłaszanie wyjątku przy użyciu standardowego mechanizmu wyjątków. Wybór mechanizmu zgłaszania błędów nie jest tak istotny jak decyzja o tym, które części systemu będą obsługiwać je bezpośrednio, a które będą jedynie Poleć książkęKup książkę 232 Rozdział 8. Programowanie defensywne zgłaszać ich wystąpienie. Jeżeli istotne jest bezpieczeństwo, należy zwrócić szczególną uwagę na każdorazowe sprawdzanie kodów stanu w procedurach wywołujących. Wywoływanie procedury (obiektu) obsługi błędu. Nieco innym podejściem jest dążenie do centralizacji obsługi błędów w globalnych procedurach lub obiek- tach. Zaletą tej techniki jest scentralizowanie odpowiedzialności, które ułatwia debugowanie. Wadą jest natomiast to, że o takim wspólnym mechanizmie wie cały program i cały program jest z nim powiązany. Jeżeli kiedykolwiek pojawi się potrzeba użycia kodu w innym systemie, wymagane będzie przeniesienie razem z nim pełnego mechanizmu obsługi błędów. Metoda ta ma istotny związek z bezpieczeństwem. W przypadku przepełnienia bufora atakujący może uzyskać dostęp do adresu procedury lub obiektu obsłu- gującego błędy. Rozwiązanie to staje się więc zagrożeniem od chwili, gdy w pra- cującej aplikacji wystąpi takie przepełnienie. Wyświetlanie komunikatu błędu bez względu na miejsce wystąpienia pro- blemu. To podejście minimalizuje ilość pracy, którą trzeba włożyć w zaprogra- mowanie mechanizmu obsługi błędów. Może ono jednak zarazem prowadzić do rozproszenia komunikatów interfejsu użytkownika po całej aplikacji, co utrudnia zapewnienie mu spójności, separowanie UI od reszty systemu i loka- lizowanie oprogramowania. Należy też uważać, aby komunikaty błędów nie zawierały informacji pomocnych osobom zainteresowanym przełamaniem zabezpieczeń systemu. Ich zawartość może być dużą pomocą dla doświadczo- nego włamywacza. Lokalna obsługa błędów. W niektórych projektach najlepszym rozwiązaniem jest lokalne obsługiwanie błędów. Decyzję o wyborze określonej metody podej- muje programista projektujący i implementujący tę część systemu, w której błąd może wystąpić. Podejście takie zapewnia poszczególnym programistom zespołu bardzo dużą swobodę, ale tworzy istotne zagrożenie tym, że działanie całego systemu nie będzie spełniać wymagań dotyczących poprawności lub odporności (więcej na ten temat za chwilę). Zależnie od wybieranych przez programistów metod na mniejszą lub większą skalę może wystąpić rozproszenie kodu interfejsu użytkownika po całym systemie. Naraża to program na wszelkie problemy związane z niejednolitym systemem wyświetlania komunikatów błędów. Przerwanie pracy programu. Niektóre systemy w chwili napotkania błędu przerywają pracę. Rozwiązanie takie może być najlepsze, gdy ich działanie ma wpływ na bezpieczeństwo ludzi. Przykładowo, jaka powinna być reakcja sys- temu, który steruje aparaturą naświetlającą stosowaną w leczeniu chorych na raka, jeżeli otrzyma on złe dane dotyczące dawkowania? Czy może użyć poprzedniej poprawnej wartości? Czy może zastosować najbliższą wartość poprawną? Czy może skorzystać z wartości neutralnej? W tym przypadku prze- rwanie pracy jest najlepszym rozwiązaniem. Lepiej uruchomić urządzenie ponownie, niż ryzykować poddanie pacjenta niewłaściwie dobranej dawce promieniowania. Poleć książkęKup książkę 8.3. Mechanizmy obsługi błędów 233 Podobne podejście można wykorzystać dla poprawienia bezpieczeństwa systemu Microsoft Windows. Normalnie, gdy dziennik zabezpieczeń jest pełny, Windows kontynuuje pracę. Można jednak wprowadzić taką konfigurację, w której zapeł- nienie dziennika spowoduje zatrzymanie serwera. Rozwiązanie to może być najlepszym, gdy bezpieczeństwo środowiska jest na pierwszym miejscu. Poprawność a odporność Jak pokazują przykłady gry wideo i urządzenia naświetlającego, wybór metody obsługi błędów w dużej mierze zależy od rodzaju oprogramowania. Przykłady te zwracają też uwagę na fakt, że pewne metody sprzyjają zachowaniu popraw- ności kodu, podczas gdy inne prowadzą do wyższej odporności programu. Programiści używają tych terminów dość nieformalnie, ale poprawność i odpor- ność to w istocie dwa przeciwieństwa. Poprawność (ang. corectness) oznacza, że program nigdy nie zwraca wyniku, który nie jest dokładny, i jego brak jest uzna- wany za lepszy niż jakikolwiek jego substytut. Odporność (ang. robustness) to pojęcie oznaczające, że zawsze podejmowane są działania mające na celu pod- trzymanie funkcjonowania systemu, nawet jeżeli prowadzi to czasem do niedo- kładnych wyników. W aplikacjach, których praca ma związek z bezpieczeństwem ludzi, zazwyczaj preferowana jest poprawność. Lepiej nie zwracać wyniku, niż zwracać błędny. Urządzenie naświetlające jest dobrym przykładem takiego systemu. W programach użytkowych często bardziej ceniona jest odporność — jakikol- wiek wynik jest lepszy niż całkowite zamknięcie aplikacji. Używany przeze mnie edytor tekstu od czasu do czasu wyświetla obcięty fragment wiersza przy dolnej krawędzi okna. Czy wystąpienie takiej sytuacji powinno spowodować zam- knięcie edytora? Nie. Wiem, że gdy tylko przewinę zawartość okna w górę lub w dół, ekran zostanie odświeżony i wszystko wróci do normy. Obsługa błędów a projekt wysokiego poziomu Gdy dostępnych jest tak wiele możliwości, trzeba zwracać szczególną uwagę na spójność reguł obsługi błędów w całym programie. Powyższe mechanizmy mają wpływ na to, w jakim stopniu oprogramowanie spełnia wymagania w za- kresie poprawności, odporności i innych cech niefunkcjonalnych. Wybór ogól- nego schematu postępowania w przypadku złych parametrów to decyzja podej- mowana na poziomie architektury lub projektu wysokiego poziomu. Po dokonaniu wyboru zasad obsługi błędów należy ich konsekwentnie prze- strzegać. Jeżeli decydujesz, że ich obsługę zapewnia kod wysokiego poziomu, a kod niskiego poziomu tylko je zgłasza, musisz zadbać o to, aby pierwszy z nich faktycznie pracował z błędami. Niektóre języki pozwalają ignorować fakt, że funkcja zwraca kod błędu — w C++ zwracana wartość w ogóle nie musi zostać użyta — nie jest to jednak cecha, z której warto korzystać. Zawsze sprawdzaj wartość zwracaną przez funkcję i wszelkie inne kody błędów. Nawet gdy nie oczekujesz, by funkcja mogła kiedykolwiek zgłosić błąd, sprawdzaj. Ochrona przed nieoczekiwanymi błędami to właśnie istota koncepcji programowania defensywnego. Poleć książkęKup książkę 234 Rozdział 8. Programowanie defensywne Porady te w równej mierze stosują się do funkcji systemowych, jak i do funkcji, które definiujesz samodzielnie. O ile nie wprowadziłeś w architekturze ogól- nej zasady, że błędy wywołań systemowych nie będą wykrywane, sprawdzaj wartość kodu błędu po każdym wywołaniu. W przypadku wystąpienia nie- prawidłowości programiście natychmiast powinna zostać udostępniona infor- macja o numerze błędu i jego standardowy opis. 8.4. Wyjątki Wyjątki to specyficzny mechanizm, który umożliwia przekazywanie informacji o błędach lub nietypowych zdarzeniach do kodu wywołującego. Gdy w trakcie wykonywania procedury rozpoznana zostaje nieoczekiwana sytuacja, do której obsługi nie jest ona przygotowana, procedura „wyrzuca” (ang. throws) wyją- tek — staje w miejscu i zaczyna krzyczeć: „Nie wiem, co z tym zrobić! Mam nadzieję, że ktoś się tym zajmie!”. Kod nieznający kontekstu, w którym wystąpił błąd, może w ten sposób przekazać kontrolę innym częściom systemu, posiada- jącym wiedzę, która pozwoli zinterpretować sytuację i podjąć rozsądne działania. Wyjątki można także wykorzystać do porządkowania zawiłej logiki na pewnym odcinku kodu. Ilustruje to przykład „Przepisać kod z użyciem try-finally” w pod- rozdziale 17.3. Ogólnie rzecz biorąc, mechanizm wyjątku polega na tym, że procedura używa polecenia throw (wyrzuć), aby go zgłosić, i przekazuje jed- nocześnie obiekt wyjątku. Kod w innej procedurze, wyżej w hierarchii wy- wołań, używa bloku try-catch (próbuj-przechwyć), aby wyjątek przechwycić. Implementacje mechanizmu wyjątków w najpopularniejszych językach nie są jednolite. W tabeli 8.1 zaprezentowane zostało zestawienie podstawowych róż- nic między trzema z nich. Jest coś, co łączy wyjątki z dziedziczeniem: oba te mechanizmy, odpowiednio stosowane, pozwalają zmniejszyć złożoność, jednak lekkomyślne ich traktowa- nie szybko prowadzi do powstania kodu, który jest niemal zupełnie niezro- zumiały. Na kolejnych stronach zebrane zostały wskazówki zwracające uwagę na korzyści płynące ze stosowania wyjątków oraz problemy, które mogą wystą- pić przy nadużywaniu tego mechanizmu. Używaj wyjątków do powiadamiania innych części programu o błędach, które nie powinny być ignorowane. Największą zaletą wyjątków jest dawana przez n
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Kod doskonały. Jak tworzyć oprogramowanie pozbawione błędów. Wydanie II
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ą: