Zarządzanie kodem projektu. Tworzenie i utrzymywanie czystego kodu Paweł Stroiński,

Podobne dokumenty
Programowanie obiektowe

Programowanie obiektowe

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

C++ Przeładowanie operatorów i wzorce w klasach

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Technologie obiektowe

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Wykład 8: klasy cz. 4

PARADYGMATY PROGRAMOWANIA Wykład 4

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Obiektowy PHP. Czym jest obiekt? Definicja klasy. Składowe klasy pola i metody

Czym są właściwości. Poprawne projektowanie klas

Materiały do zajęć VII

1 Podstawy c++ w pigułce.

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

Kurs WWW. Paweł Rajba.

Podstawy Programowania Obiektowego

Zadanie polega na stworzeniu bazy danych w pamięci zapewniającej efektywny dostęp do danych baza osób.

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

Język JAVA podstawy. Wykład 3, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Język programowania. Andrzej Bobyk

Wykład 5: Klasy cz. 3

DECLARE VARIABLE zmienna1 typ danych; BEGIN

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Podstawy programowania skrót z wykładów:

Modelowanie diagramów klas w języku UML. Łukasz Gorzel @stud.umk.pl 7 marca 2014

Metody Metody, parametry, zwracanie wartości

Rozdział 4 KLASY, OBIEKTY, METODY

PLAN WYNIKOWY PROGRAMOWANIE APLIKACJI INTERNETOWYCH. KL IV TI 6 godziny tygodniowo (6x15 tygodni =90 godzin ),

Dokumentacja do API Javy.

Wyjątki (exceptions)

Programowanie Obiektowe Ćwiczenie 4

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

Tablice (jedno i wielowymiarowe), łańcuchy znaków

Klasy i obiekty cz II

C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie C++ - DZIEDZICZENIE.

Programowanie obiektowe - 1.

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

TEMAT : KLASY DZIEDZICZENIE

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Technologie i usługi internetowe cz. 2

Modelowanie obiektowe

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Widoczność zmiennych Czy wartości każdej zmiennej można zmieniać w dowolnym miejscu kodu? Czy można zadeklarować dwie zmienne o takich samych nazwach?

Wyjątki. Wyjątki. Bogdan Kreczmer. Katedra Cybernetyki i Robotyki Politechnika Wrocławska

Wykład VII. Programowanie III - semestr III Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Diagram klas UML jest statycznym diagramem, przedstawiającym strukturę aplikacji bądź systemu w paradygmacie programowania obiektowego.

1 Podstawy c++ w pigułce.

Język programowania Scala / Grzegorz Balcerek. Wyd. 2. Poznań, cop Spis treści

PHP: bloki kodu, tablice, obiekty i formularze

Programowanie w Sieci Internet Blok 2 - PHP. Kraków, 09 listopada 2012 mgr Piotr Rytko Wydział Matematyki i Informatyki

Polimorfizm, metody wirtualne i klasy abstrakcyjne

4. Funkcje. Przykłady

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Szablony klas, zastosowanie szablonów w programach

Inżynieria Oprogramowania

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Programowanie obiektowe i C++ dla matematyków

Programowanie i techniki algorytmiczne

Uwagi dotyczące notacji kodu! Moduły. Struktura modułu. Procedury. Opcje modułu (niektóre)

Programowanie obiektowe

Język JAVA podstawy. wykład 2, część 1. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Aplikacje w środowisku Java

Jeśli chcesz łatwo i szybko opanować podstawy C++, sięgnij po tę książkę.

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Wstęp do Programowania 2

Programowanie obiektowe

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) { zdefiniuje. Integer::operator=(ri);

XQTav - reprezentacja diagramów przepływu prac w formacie SCUFL przy pomocy XQuery

Programowanie obiektowe Wykład 6. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14

Podstawy i języki programowania

Programowanie obiektowe, wykład nr 6. Klasy i obiekty

Programista samouk : profesjonalny przewodnik do samodzielnej nauki kodowania / Cory Althoff. Gliwice, copyright Spis treści

Liczby losowe i pętla while w języku Python

Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych. Ćwiczenie 3 stos Laboratorium Metod i Języków Programowania

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Kod doskonały : jak tworzyć oprogramowanie pozbawione błędów / Steve McConnell. Gliwice, cop Spis treści. Wstęp 15.

PHP 5 język obiektowy

Pętle. Dodał Administrator niedziela, 14 marzec :27

Programowanie w Ruby

Kompilacja javac prog.java powoduje wyprodukowanie kilku plików o rozszerzeniu.class, m.in. Main.class wykonanie: java Main

Zofia Kruczkiewicz - Modelowanie i analiza systemów informatycznych 1

Programowanie obiektowe

1. Wypisywanie danych

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

Do czego służą klasy?

Programowanie dla początkujących w 24 godziny / Greg Perry, Dean Miller. Gliwice, cop Spis treści

SUM Edukacja Techniczno Informatyczna Języki i Systemy Programowania. Wykład 3. dr Artur Bartoszewski - WYKŁAD: Języki i Systemy Programowania,

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

JAVA W SUPER EXPRESOWEJ PIGUŁCE

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy WSKAŹNIKI KLASOWE

Enkapsulacja, dziedziczenie, polimorfizm

Transkrypt:

Zarządzanie kodem projektu Tworzenie i utrzymywanie czystego kodu Paweł Stroiński, 01.2011

Plan prezentacji Wprowadzenie Definicja czystego kodu Dobre nazwy w kodzie Zrozumiały kod funkcji Komentowanie kodu jest przereklamowane Formatowanie kodu to nie problem Obiekty i struktury danych w czystym kodzie Zakończenie

Wprowadzenie Rola czystego kodu w zarządzaniu projektem informatycznym Książka Zalety tej prezentacji w porównaniu do książki

Rola czystego kodu w zarządzaniu projektem informatycznym Nic nie ma tak głębokiego i w długim czasie degradującego wpływu na prowadzenie projektu, jak zły kod. Złe harmonogramy mogą być zmodyfikowane, złe wymagania przedefiniowane. Złą dynamikę zespołu da się poprawić. Jednak zły kod psuje się, stając się coraz większym ciężarem dla zespołu. Widziałem wiele zespołów, które się rozsypywały, ponieważ w pośpiechu tworzyły złośliwe bagnisko kodu, który na zawsze zdeterminował ich przeznaczenie. ( ) Tak więc rozwiązaniem jest ciągłe zachowywanie czystości i maksymalnej prostoty kodu. Nigdy nie pozwalajmy, by zaczął się psuć.

Książka Czysty kod. Podręcznik dobrego programisty Robert C. Martin

Zalety (wady) tej prezentacji w porównaniu do książki Jest mniej kodu Trwa krócej niż przeczytanie książki Z powodu ograniczeń czasowych obejmuje Z powodu ograniczeń czasowych obejmuje około ½ (pierwszą połowę) książki

Definicja czystego kodu elegancki i efektywny prosty i bezpośredni można go łatwo zmienić posiada testy minimalny opisywany ktoś się nim przejął taki jakiego się spodziewaliśmy nie czyni go język a programista

Dobre nazwy w kodzie przedstawiają intencje bez mylących i fałszywych wskazówek bez zbyt subtelnych różnic są spójne i dobrze się sortują w IDE można je wymówić! łatwo je wyszukać bez kodowania typów bez przedrostków składników bez kodowania interfejsów bez jedno-literówek nazwy klas mają rzeczowniki nazwy metod mają czasowniki zamiast przeciążonych konstruktorów stosujemy metody fabryk dowcipne nazwy mogą nie być zrozumiane spójny leksykon bez przesady ze spójnością używamy terminologii informatycznej w drugiej kolejności używamy terminologii klienta dodajemy znaczący kontekst bez nadmiarowego kontekstu zmiana nazwy na czytelniejszą to nic złego

przedstawiają intencje int c; int studentscount; List<int> list1 = new List<int>(); List<int> temperaturevalues = new List<int>();

bez mylących i fałszywych wskazówek int hp; // hiperprzelicznik Student[] studentlist; List<Student> studentlist; List<Student> students; Student[] students;

bez zbyt subtelnych różnic Student VeryGoodStudentInThisCourseOfPhysics; Student VeryGoodStudentInThatCourseOfPhysics; void copystudents(student[] a1, Student[] a2) void copystudents(student[] source, Student[] destination) Student student; Student astudent; Student thestudent; int ECTS; int ECTSpoints; string TitleString; class StudentObject

są spójne i dobrze się sortują w IDE

można je wymówić! Nasze mózgi lubią mowę. Żeby możliwa była dyskusja o kodzie. Żeby nowe osoby w zespole rozumiały kod. class SdntRcrd126 { private DateTime admymdhms; private const string pszqint = "126"; } class Student { private DateTime admissiontimestamp; private const string recordid = "126"; }

łatwo je wyszukać, stałe zamiast liczb if (s.count > 9) const int MAX_STUDENTS_PER_YEAR = 9; if (students.count > MAX_STUDENTS_PER_YEAR)

bez kodowania typów int securitycodestring; //kiedyś ta zmienna może i była typu string

bez przedrostków składników Najnowsze IDE wyróżniają zmienne składowe. Czytając kod i tak ignorujemy przedrostki. W małych klasach i funkcjach są zbędne. class Student { private String m_name; void setname(string name) { m_name = name; } } class Student { private String name; void setname(string name) { this.name = name; } }

bez kodowania interfejsów (lepszy przyrostek Imp / Impl ) interface IComputerStudent interface ComputerStudent class ComputerStudentImp : ComputerStudent

bez jedno-literówek, które trzeba sobie potem tłumaczyć Uri r = new Uri("http://usosweb.uni.lodz.pl"); Może dla nas nie problem to zapamiętać, ale profesjonaliści piszą kod zrozumiały dla innych Wyjątkiem są liczniki pętli ale tylko i, j i k for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) for (int k = 0; k < 4; k++)

nazwy klas mają rzeczowniki class Course class CourseAdvisor class Advise class Advice Unikamy mowy-trawy jak np. Manager, Processor, Data, Info class CourseProcessor

nazwy metod mają czasowniki counter.submit(); get przed akcesorami, set przed mutatorami, is przed predykatami if (course.iscompleted()) totalcompletedhours += course.gettotalhours(); counter.settotal(totalcompletedhours);

zamiast przeciążonych konstruktorów stosujemy metody fabryk o nazwach pochodzących od argumentów, np. City.FromName( Łódź ) tworzy obiekt City z nazwą Łódź City lodz = new City("Łódź"); City lodz = City.FromName("Łódź");

dowcipne nazwy mogą nie być zrozumiane

spójny leksykon jedno słowo na zagadnienie if (ListenerManager.retrieveCurrent() == StudentController.getActual())

bez przesady ze spójnością jedno zagadnienie na słowo course.title = "C#"; student.title = "Wiesław Czyżewski";

używamy terminologii informatycznej z tzw. dziedziny rozwiązania Możemy swobodnie stosować terminy informatyczne (jak nazwy algorytmów czy wzorców) bo czytelnicy kodu programiści je znają

w drugiej kolejności używamy terminologii klienta z tzw. dziedziny problemu Tylko jeśli nie mamy na dane pojęcie terminu technicznego Gdy istnieją dwa terminy w języku klienta na jedno pojęcie, stosujemy tylko jeden termin Im bardziej kod jest z koncepcji dziedziny problemu, tym częściej należy stosować jej terminy

dodajemy znaczący kontekst (wydzielamy klasy i funkcje!) name = "Wiesław Czyżewski"; id = 15640; student.name = "Wiesław Czyżewski"; student.id = 15640; public Course advisecourse() { if (student.likesmath()) { if (student.knowshowtoadd) public Course advisecourse() { if (student.likesmath()) return advisemathcourses();

bez nadmiarowego kontekstu jako wspólnego przedrostka na aplikację/moduł class XYZAPPStudent class XYZAPPCourse class StudentAddress class LecturerAddress class Address

zmiana nazwy na czytelniejszą to nic złego (refactoring) I tak korzystamy z podpowiedzi IDE nie pamiętając większość nazw

Zrozumiały kod funkcji (1) bardzo krótkie (najlepiej tylko 2 4 wiersze a maksymalnie 20) bloki w instrukcjach warunkowych i pętlach mają po jednym wierszu często wywołujemy kolejne funkcje o opisowych nazwach struktura prosta, bez zagnieżdżeń, czyli maksymalnie 2 wcięcia złota zasada: FUNKCJE POWINNY WYKONYWAĆ JEDNĄ OPERACJĘ. POWINNY ROBIĆ TO DOBRZE. POWINNY ROBIĆ TYLKO TO aby kod czytało się jak artykuł prasowy (piramidę od ogółu do szczegółu ) bezpośrednio pod funkcją znajdują się funkcje będące od niej o jeden poziom abstrakcji niżej

Zrozumiały kod funkcji (2) ukrywamy instrukcje switch w podstawie fabryki abstrakcyjnej, używając polimorfizmu jak najmniej argumentów (najlepiej zero, 3 w ostateczności) typy funkcji jednoargumentowych argumenty przekazujące true/false są złe. lepiej podzielić na osobne funkcje dla true i false funkcje dwuargumentowe są trudniejsze do zrozumienia niż jednoargumentowe, dlatego często warto zrezygnować z jednego z argumentów korzystając z klas gdy funkcja potrzebuje więcej niż 2 argumentów możliwe, że powinniśmy część z nich umieścić w osobnej klasie

Zrozumiały kod funkcji (3) listy argumentów jak w funkcji printf są w porządku nawet jeśli pozornie przekazujemy ich np. 5, bo tak samo traktujemy wszystkie argumenty z listy argumentów kodujmy porządek i przeznaczenie argumentów w nazwie funkcji, tworząc pary czasownik- rzeczownik, np. write(name) zamieniamy na writefield(name), a assertequals(expected, actual) zamieniamy na assertexpectedequalsactual(expected, actual)! funkcje powinny być bez efektów ubocznych, ponieważ powoduje to naruszenie zasady wykonywania jednej operacji oraz generuje sprzężenie czasowe które jest niebezpieczne (jeśli już sprzężenie czasowe jest nie do uniknięcia to powinno być podane w nazwie funkcji)

Zrozumiały kod funkcji (4) funkcja nie powinna jednocześnie czegoś robić i sprawdzać coś zwracając jakąś wartość, nawet typu true/false należy w tym przypadku zrobić osobne funkcje rozdzielić polecenie od zapytania zawartość bloków try-catch warto wyodrębniać do osobnych funkcji, które zajmują się tylko wywołaniem kłopotliwej funkcji owiniętym w blok try-catch zasada DRY: nie powtarzaj się, bo to źródło wszelkiego zła w oprogramowaniu najłatwiej wyeliminować powielanie tego samego kodu w dwóch metodach tworząc nową metodę z tym kodem pisanie zrozumiałych funkcji

często wywołujemy kolejne funkcje o opisowych nazwach (sama tworzy się dokumentacja!) dla małych funkcji łatwiej o precyzyjną nazwę nie bójmy się długich nazw próbujmy różnych nazw spójne nazwy, opowiadające historię

złota zasada: FUNKCJE POWINNY WYKONYWAĆ JEDNĄ OPERACJĘ. POWINNY ROBIĆ TO DOBRZE. POWINNY ROBIĆ TYLKO TO czy kod ma tylko jeden poziom abstrakcji (tuż pod poziomem abstrakcji nazwy funkcji)? czy da się z niego jeszcze coś sensownego wyodrębnić? czy da się go sensownie podzielić na sekcje?

ukrywamy instrukcje switch w podstawie fabryki abstrakcyjnej, używając polimorfizmu public decimal getmonthtuitionfee (Student student) { switch (student.type) { case StudentType.Day: return 0; case StudentType.Evening: return 250; public decimal getyeartuitionfee (Student student) abstract public class Student { abstract public decimal getmonthtuitionfee(); abstract public decimal getyeartuitionfee(); public interface StudentFactory { Student makestudent(studentrecord r); public class StudentFactoryImpl : StudentFactory { public Student makestudent(studentrecord r) { switch (r.type) { case StudentType.Day: return new DayStudent(); case StudentType.Evening: return new EveningStudent();

typy funkcji jednoargumentowych zadajemy pytanie na temat argumentu, np. fileexists( wykład.doc ) operujemy na argumencie, przekształcając go w coś innego i zwracając(!) wynik, np. fileopen( wyklad.doc ) zdarzenia wykorzystujące argumenty do zmiany stanu systemu, ale niczego nie zwracające, np. passwordattemtfailedntimes(int attemps) nie należy nadużywać innych typów unikać, np. nie używać argumentu wyjściowego zamiast zwracanej wartości, poza tym jeżeli funkcja musi zmieniać stan czegokolwiek, powinna zmieniać stan własnego obiektu

funkcje dwuargumentowe są trudniejsze do zrozumienia niż jednoargumentowe, dlatego często warto zrezygnować z jednego z argumentów korzystając z klas: można przenieść dwuargumentową metodę do klasy jednego z argumentów można umieścić jeden z argumentów w zmiennej składowej bieżącej klasy można wyodrębnić nową klasę i jeden z argumentów przekazywać jej w konstruktorze

funkcja nie powinna jednocześnie czegoś robić i sprawdzać coś zwracając jakąś wartość, nawet typu true/false należy w tym przypadku zrobić osobne funkcje rozdzielić polecenie od zapytania subtelnym przykładem jest stosowanie kodów błędów nie należy tego robić, od tego są wyjątki stosowanie kodów błędów zamiast mechanizmu wyjątków znacznie komplikuje kod

zawartość bloków try-catch warto wyodrębniać do osobnych funkcji, które zajmują się tylko wywołaniem kłopotliwej funkcji owiniętym w blok try-catch w ten sposób nie mieszamy przetwarzania błędów ze zwykłym przetwarzaniem obsługa błędów już jest jedną operacją (patrz obsługa błędów już jest jedną operacją (patrz złota zasada) więc jeżeli w funkcji jest słowo try, to powinno ono być jej pierwszym słowem, a funkcja powinna kończyć się na bloku catch/finally!

zasada DRY: nie powtarzaj się, bo to źródło wszelkiego zła w oprogramowaniu powtarzanie powoduje rozdęcie kodu wymaga wielokrotnych modyfikacji zwiększa możliwość popełnienia błędu

pisanie zrozumiałych funkcji pierwszy szkic funkcji jest często zbyt długi, skomplikowany, ma wiele wcięć, zagnieżdżonych pętli i argumentów, o dowolnych nazwach i powielonym kodzie należy wydzielać funkcje, zmieniać nazwy, eliminować powtórzenia, zmniejszać metody i je przestawiać, a czasem nawet wyodrębniać całe klasy

Komentowanie kodu jest przereklamowane Nie trać czasu na komentowanie złego kodu. Popraw go Przykład: zamiast pisać komentarz objaśniający czynność, umieść tę czynność w nowej, dobrze nazwanej funkcji Wyjątki od reguły kiedy można komentować Reguła złe komentarze

Wyjątki od reguły kiedy można komentować (1) copyright u góry pliku (cytować licencję tylko z nazwy) komentarz informacyjny, np. obok wyrażenia regularnego w komentarzu jest akceptowany format tłumaczymy się z naszych decyzji jak coś ma działać (wyjaśnianie zamierzeń), np.: zasady sortowania w funkcji porównującej w nietypowym przypadku cel użycia absurdalnie dużej pętli w przypadku testowym komentarze objaśniające zastosowanie niejasnego kodu z biblioteki standardowej lub innej której innej nie możemy zmienić aby jej zastosowanie było czytelniejsze

Wyjątki od reguły kiedy można komentować (2) komentarze ostrzegające o tym co by było gdybyśmy zmienili kod w narzucający się sposób np. dlaczego wyłączyliśmy przypadek testowy (bo jest czasochłonny) dlaczego w statycznej metodzie inicjalizujemy zmienną lokalną niestatycznie (bo nie jest bezpieczna dla wątków) komentarze TODO mówiące o rzeczach do zrobienia w danym miejscu kodu, pod warunkiem że są regularnie przeglądane i usuwane komentarze podkreślające wagę naprawdę istotnych operacji, które pozbawione komentarza wydają się niekonsekwencją i ktoś mógłby je usunąć (np. wywołanie funkcji Trim()) komentarze służące do generowania dokumentacji publicznego API, pod warunkiem że są rzeczywiście dobre

Reguła złe komentarze (1) Jeśli musimy przeglądać inne moduły żeby zrozumieć komentarz to jest to bełkot Komentarze powtarzające kod Z natury rzeczy są mniej precyzyjne niż kod bo język naturalny jest mniej precyzyjny niż kod (mogą więc być mylące), a dłużej się je czyta niż klarowny kod Nie dodają żadnej informacji, tylko zaciemniają kod Dezinformujące komentarze (np. mówiące tylko część prawdy, nieprecyzyjne) Wymaganie komentarzy np. dla wszystkich funkcji lub zmiennych bo tak i już zaciemniają kod, powstają powszechne pomyłki i dezorganizacja Dzienniki zmian na początku modułu pogarszają jego czytelność od tego są logi SVN

Reguła złe komentarze (2) Szum informacyjny Np. komentowanie konstruktora domyślnego konstruktor domyślny Szum informacyjny jest ignorowany automatycznie przez czytających, przez co może stać się nieaktualny i mylący Kiedy mamy ochotę napisać komentarz będący szumem informacyjnym, lepiej przeznaczyć tę chwilę na wyczyszczenie kodu Komentarz będący dokumentacją publicznego API sprowadzający się do powtarzania nazw z kodu to straszny szum Komentarz objaśniający nieczytelny kod który można zamienić na wyodrębnienie funkcji lub dodanie kilku zmiennych uczytelniając kod Rzucające się w oczy nagłówki typu // Nagłówek ///////////////////////////////////////// (znaczniki pozycji, transparenty) przydatne bardzo rzadko i jeśli już to bez tych ukośników na końcu. Powinny być używane oszczędnie tylko gdy są naprawdę potrzebne

Reguła złe komentarze (3) Komentowanie klamry zamykającej informując co było przy klamrze otwierającej 2 kilometry wcześniej. Przecież mamy krótkie funkcje, po co nam to? Komentarze oznaczające autorstwo dopisanych fragmentów kodu (śmieci które później zalegają w kodzie nieprecyzyjne i zbędne) od tego jest SVN Usuwanie kodu przez jego zakomentowanie. Skąd później inne osoby mają wiedzieć dlaczego ten kod pozostał w tej formie? Może jest istotny? Więc nikt go nie usunie. I taki zakomentowany kod później zalega. Jeśli chcemy zachować ten kod na wszelki wypadek od tego jest SVN Stosowanie kodu HTML w komentarzach. Jeśli używamy narzędzi do generowania dokumentacji z komentarzy to one powinny zadbać o jej sformatowanie, a komentarz w kodzie powinien być czytelny dla czytającego kod

Reguła złe komentarze (4) Komentarz lokalny opisujący informacje nielokalne. Np. domyślną wartość parametru w funkcji która nie określa tej domyślnej wartości. Komentarz taki najprawdopodobniej stanie się nieaktualny. Jeśli już komentujemy, to tylko to co dzieje się tuż obok Za duży komentarz zamiast cytować cały opis specyfikacji RFC wystarczy napisać jej numer, należy też unikać wszelkich luźnych opisów, opisów historycznych itp. Komentarz nieprecyzyjnie powiązany z elementami kodu, np. nie wskazujący precyzyjnie zmiennych komentarz do kodu wykonującego obliczenia na bazie niejasnych zmiennych Komentarze opisujące w nagłówkach funkcje nie są potrzebne, kiedy mamy krótkie dobrze nazwane funkcje wykonujące pojedyncze operacje Komentarze służące do generowania dokumentacji niepublicznego kodu (wewnątrz systemu) są najczęściej nieprzydatne. Poza tym są podatne na błędy i rozpraszające

Formatowanie kodu to nie problem (1) Formatowanie pomaga w uporządkowaniu i czytelności kodu, które pomagają w łatwości utrzymania i rozszerzania kodu, dlatego jest ważne Małe pliki łatwiej zrozumieć niż duże, więc rozsądnym zaleceniem jest utrzymywanie plików o długości przeciętnej 200 wierszy i maksymalnej 500 wierszy Pliki naśladujące artykuły w gazetach Puste wiersze oddzielają osobne koncepcje takie jak deklaracja pakietu/przestrzeni nazw, importy i każda z funkcji Nie stosujemy pustych wierszy tam gdzie nie trzeba, np. nie ma sensu rozdzielać pustym wierszem deklaracji dwóch zmiennych instancyjnych

Formatowanie kodu to nie problem (2) Jeśli między koncepcjami jest bliski związek, powinny być również blisko siebie w kodzie Znane i oczekiwane stałe należy trzymać w funkcjach znajdujących się na wysokim poziomie i przekazywać do funkcji znajdujących się na niższym poziomie zamiast trzymać je w funkcjach niższego poziomu W poziomie nie należy przekraczać długości wierszy 120 znaków, żeby nie przewijać kodu poziomo Spacje stosujemy do oddzielenia osobnych elementów, np. jest sensowne oddzielenie spacją operatora przypisania po obu stronach albo argumentów wywołania funkcji, ale nie ma sensu oddzielać spacją nawiasu od nazwy funkcji

Formatowanie kodu to nie problem (3) Nie wyrównujemy w kilku kolumnach deklaracji lub przypisań (stosujemy pojedyncze spacje) Wcięcia stosujemy do podkreślenia zakresów, np. instrukcje na poziomie plików nie są nigdy wcinane, ale już kod w obrębie metody jest zawsze wcinany, a implementacje instrukcji if umieszczamy na kolejnym poziomie wcięcia. Bez wcięć kod byłby nieczytelny Nie łamiemy wcięć i nie łączymy wierszy nawet dla bardzo krótkich instrukcji if, pętli while lub jednowierszowych metod Zawsze wcinamy puste zakresy, np. średnik na końcu pustych pętli umieszczamy wcięty w osobnym wierszu

Formatowanie kodu to nie problem (4) Zespół programistów używa wspólnych zasad formatowania kodu. Należy ustalić takie zasady jak miejsce umieszczania klamr, wielkość wcięcia, sposób nazywania klas, metod, zmiennych itd. i wprowadzić je do formatera kodu Wg zasad formatowania kodu autora książki pojedyncze wcięcia mają dwie spacje, klamry otwierające nie są w osobnym wierszu, nazwy klas zaczynają się wielką literą, nazwy metod i zmiennych małą literą, zmienne instancyjne są na początku klas

Pliki naśladujące artykuły w gazetach Nazwa modułu jak tytuł artykułu. Prosta, sugestywna, informująca nas czy jesteśmy we właściwym module Górne partie kodu źródłowego jak pierwszy akapit artykułu. Algorytmy i koncepcje najwyższego poziomu. Dalsze partie kodu jak kolejne akapity artykułu. Coraz więcej szczegółów w miarę schodzenia w dół, aż do funkcji oraz szczegółów najniższego poziomu na samym dole Użyteczne jest, że artykuły są krótkie, rzadko na całą stronę

Jeśli między koncepcjami jest bliski związek, powinny być również blisko siebie w kodzie Zmienne zadeklarowane jak najbliżej miejsca ich użycia Zmienne lokalne deklarujemy na początku krótkich funkcji Zmienne sterujące pętli deklarujemy zazwyczaj wewnątrz instrukcji pętli. Rzadko na początku bloku lub w dłuższych funkcjach przed samą pętlą Zmienne instancyjne deklarujemy zawsze na początku klasy (albo zawsze na końcu, ale konsekwentnie). Nigdy w środku, bo łatwo je pominąć Funkcja wywołująca powinna być powyżej funkcji wywoływanej w bliskiej od niej odległości (najlepiej bezpośrednio przed nią). W ten sposób łatwiej znaleźć wywoływane funkcje i moduł jest czytelniejszy Jeśli funkcje wykonują podobne operacje, powinny ze sobą sąsiadować

Obiekty i struktury danych w czystym kodzie Tworzenie abstrakcyjnego sposobu dostępu do danych obiektu nie polega na prostym dodaniu getterów i setterów do zmiennych Kod obiektowy i kod proceduralny (korzystający ze struktur danych) są uzupełniającymi się przeciwieństwami. Stosujmy korzystniejszy dla naszego przypadku Źle, kiedy moduł wie coś o wewnętrznej budowie obiektów z których korzysta Unikajmy hybryd przypominających trochę struktury danych, a trochę obiekty Niektóre formy struktur danych

Tworzenie abstrakcyjnego sposobu dostępu do danych obiektu nie polega na prostym dodaniu getterów i setterów do zmiennych Zamiast tego trzeba udostępnić możliwość pracy z istotą tych danych, aby implementacja była dla korzystającego z obiektu nieważna i niewidoczna. Należy określić najlepszy sposób reprezentowania danych zawartych w obiekcie, np. wartość procentowa zamiast absolutnej, przeliczanie punktu na współrzędne prostokątne i kątowe, wymuszenie jednoczesnego określenia obu współrzędnych.

Kod obiektowy i kod proceduralny (korzystający ze struktur danych) są uzupełniającymi się przeciwieństwami. Stosujmy korzystniejszy dla naszego przypadku Kod obiektowy ukrywa dane. Oferuje zamiast nich warstwę abstrakcji w postaci zbioru funkcji do operowania na tych danych Kod proceduralny składa się z procedur operujących na strukturach danych, które to nie ukrywają danych i nie mają istotnych funkcji Mocną stroną kodu obiektowego jest dodawanie nowego typu danych tworzymy tylko nową klasę (reszty kodu nie zmieniając) Mocną stroną kodu proceduralnego jest dodawanie nowej operacji tworzymy tylko nową funkcję (reszty kodu nie zmieniając) Słabą stroną kodu obiektowego jest dodawanie nowej operacji musimy dodać ją we wszystkich klasach Słabą stroną kodu proceduralnego jest dodawanie nowego typu danych musimy dodać go we wszystkich funkcjach

Źle, kiedy moduł wie coś o wewnętrznej budowie obiektów z których korzysta Prawo Demeter: metoda powinna korzystać jedynie z metod bezpośrednio udostępnianych przez: macierzysty obiekt, zmienne instancyjne macierzystego obiektu, obiekty będące argumentami wywołania, obiekty samodzielnie utworzone. Prawo Demeter zabrania korzystać z metod niewymienionych obiektów ( rozmawiajmy z przyjaciółmi a nie obcymi ), ale nie dotyczy struktur danych (możemy dowolnie dostawać się do ich danych) Przykład: wrak pociągu (wagon.getwagon1().getwagon2().getwagon3()) Jeśli są to struktury danych, wystarczy wydzielić lokalne zmienne Jeśli są to obiekty, należy ukryć strukturę, udostępniając kompletne operacje na odpowiednim poziomie. Obiekt powinien coś robić dla nas zamiast udostępniać nam swoje szczegóły wewnętrzne

Unikajmy hybryd przypominających trochę struktury danych, a trochę obiekty (np. zmienne publiczne lub proste gettery i settery do zmiennych wymieszane z metodami realizującymi operacje) Hybrydy mają same słabe strony utrudniają dodawanie nowych typów danych i operacji. Trzeba zdecydować, czy w naszym przypadku bardziej potrzebna jest ochrona funkcji, czy typów.

Niektóre formy struktur danych Klasa nie zawierająca nic poza publicznymi zmiennymi. Można to nazwać obiektem transferu danych (DTO), który ma zastosowania takie jak: komunikacja z bazą danych, komunikacja przez sockety itp. Stosuje się je też na początku przekształceń kodu przechodzących od surowych danych z bazy do obiektów Javowy bean zmienne prywatne z publicznymi akcesorami i mutatorami. Fałszywa hermetyzacja Active Record. Zbudowane jak DTO lub beany, ale mają też metody nawigacyjne np. save, find. Udostępniają całą zawartość baz danych Nie należy umieszczać w nich operacji Zamiast tego można umieszczać je jako ukryte dane obiektów udostępniających operacje

Zakończenie Pytania Dziękuję za uwagę