Object Calisthenics (ZOSIA, Przesieka 2017 PL)

Preview:

Citation preview

We design and developcomplex business applications

Object Calisthenics

czyli jak uczynić nasz kod czytleniejszym

Grzegorz Byrka

ZOSIA 2017

Dlaczego warto pisać kod dobrej jakości? (poza tym, że nam za to płacą)

3

● Poprawa czytelności i zrozumiałości

• Ułatwione wdrażanie nowych osób do zespołu• Łatwiejsze zarządzanie i modyfikowanie istniejących funkcjonalności

Dlaczego warto pisać kod dobrej jakości?

4

● Poprawa czytelności i zrozumiałości

• Ułatwione wdrażanie nowych osób do zespołu• Łatwiejsze zarządzanie i modyfikowanie istniejących funkcjonalności

● Zmniejszona potrzeba tworzenia dokumentacji technicznej

• Z wyłączeniem dokumentowania administracji systemem

Dlaczego warto pisać kod dobrej jakości?

5

● Poprawa czytelności i zrozumiałości

• Ułatwione wdrażanie nowych osób do zespołu• Łatwiejsze zarządzanie i modyfikowanie istniejących funkcjonalności

● Zmniejszona potrzeba tworzenia dokumentacji technicznej

• Z wyłączeniem dokumentowania administracji systemem

● Łatwiejsze testowanie

Dlaczego warto pisać kod dobrej jakości?

6

● Poprawa czytelności i zrozumiałości

• Ułatwione wdrażanie nowych osób do zespołu• Łatwiejsze zarządzanie i modyfikowanie istniejących funkcjonalności

● Zmniejszona potrzeba tworzenia dokumentacji technicznej

• Z wyłączeniem dokumentowania administracji systemem

● Łatwiejsze testowanie

● Poprawiona reużywalność poszczególnych metod

Dlaczego warto pisać kod dobrej jakości?

7

DISCLAIMER

8

Przedstawione zasady mają charakter zaleceń,należy dostosować je do natury własnego projektu

Zasady Object Calisthenics

9

1. Tylko jeden poziom wcięcia na metodę

Zasady Object Calisthenics

10

class Board { String board() { StringBuffer buf = new StringBuffer(); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { buf.append(data[i][j]); buf.append("\n"); } } return buf.toString(); }}

11

class Board { String board() { // 0 StringBuffer buf = new StringBuffer(); for (int i = 0; i < 10; i++) { // 1 for (int j = 0; j < 10; j++) { // 2 buf.append(data[i][j]); buf.append("\n"); } } return buf.toString(); }}

12

class Board { String board() { StringBuffer buf = new StringBuffer(); collectRows(buf); return buf.toString(); }

void collectRows(StringBuffer buf) { for (int i = 0; i < 10; i++) { collectRow(buf, i); } }

void collectRow(StringBuffer buf, int row) { for (int i = 0; i < 10; i++) { Buf.append(data[row][i]); buf.append("\n"); } }}

13

● Dokładniejsze nazewnictwo metod

● Krótsze metody

● Re-używalne metody

Zalety:

14

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

Zasady Object Calisthenics

15

if (…) { …} elseif (…) { …} elseif (…) { …} elseif (…) { …} elseif (…) { …} elseif (…) { …} elseif (…) { …} elseif (…) { …} else { …}

16

17

public void login(String username, String password) { if (userRepository.isValid(username, password)) { redirect("homepage"); } else { addFlash("error", "Bad credentials"); redirect("login"); }}

18

public void login(String username, String password) { if (userRepository.isValid(username, password)) { return redirect("homepage"); }

addFlash("error", "Bad credentials"); return redirect("login");}

19

public void login(String username, String password) { String redirectRoute = "homepage";

if (!userRepository.isValid(username, password)) { addFlash("error", "Bad credentials"); redirectRoute = "login"; }

redirect(redirectRoute);}

20

● Mniej upakowany kod

● Mniejsza złożoność logiczna

● Czytelność

● Unikamy duplikowania kodu

Zalety:

21

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

Zasady Object Calisthenics

22

Class Date { public boolean check(int year, int month, int day){ ... }}

// 9th of October or 10th of SeptemberDate.check(2017, 10, 9);

23

Class Date { public boolean check(Year year, Month month, Day day){ ... }}

Date.check(new Year(2017), new Month(10), new Day(9));

24

● Zabezpieczenie przed niewykryciem błędnego użycia metody

● Type hinting

Zalety:

25

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

Zasady Object Calisthenics

26

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

– Każda kolekcja musi być opakowana w swoją klasę, która będzie odpowiedzialna za wszystkie operacje

związane z tą kolekcją (filtrowanie, mapowanie, łączenie kolekcji, … )

Zasady Object Calisthenics

27

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

– Każda kolekcja musi być opakowana w swoją klasę, która będzie odpowiedzialna za wszystkie operacje

związane z tą kolekcją (filtrowanie, mapowanie, łączenie kolekcji, … )

– Żadna klasa z kolekcją nie może zawierać innych zmiennych

Zasady Object Calisthenics

28

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię (lub –> w PHP)

Zasady Object Calisthenics

29

class Piece { public String representation;}

class Location { public Piece current;}

class Board { public String boardRepresentation() { StringBuilder buf = new StringBuilder();

for (Location loc : squares()) { buf.append(loc.current.representation.substring(0, 1)); }

return buf.toString(); }}

30

class Piece { private String representation;

public String character() { return representation.substring(0, 1); }

public void addTo(StringBuilder buf) { buf.append(character()); }}

31

class Piece { private String representation;

public String character() { return representation.substring(0, 1); }

public void addTo(StringBuilder buf) { buf.append(character()); }}

class Location { private Piece current;

public void addTo(StringBuilder buf) { current.addTo(buf); }}

32

class Piece { private String representation;

public String character() { return representation.substring(0, 1); }

public void addTo(StringBuilder buf) { buf.append(character()); }}

class Location { private Piece current;

public void addTo(StringBuilder buf) { current.addTo(buf); }}

33

class Piece { private String representation;

public String character() { return representation.substring(0, 1); }

public void addTo(StringBuilder buf) { buf.append(character()); }}

class Location { private Piece current;

public void addTo(StringBuilder buf) { current.addTo(buf); }}

34

class Board { public String boardRepresentation() { StringBuilder buf = new StringBuilder();

for (Location location : squares()) { location.addTo(buf); }

return buf.toString(); }}

● Przestrzeganie zasad prywatności

● Zgodność z Prawem Demeter (regułą ograniczonej interakcji: „Rozmawiaj tylko z bliskimi przyjaciółmi”)

Zalety:

35

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

Zasady Object Calisthenics

36

if (sx >= sy) { if (sx > strSysMatImgW) { ny = strSysMatImgW * sy / sx; nx = strSysMatImgW; } if (ny > strSysMatImgH) { nx = strSysMatImgH * sx / sy; ny = strSysMatImgH; }} else { if (sy > strSysMatImgW) { nx = strSysMatImgH * sx / sy; ny = strSysMatImgH; } if (nx > strSysMatImgH) { ny = strSysMatImgW * sy / sx; nx = strSysMatImgW; }} 37

if (sx >= sy) { if (sx > strSysMatImgW) { ny = strSysMatImgW * sy / sx; nx = strSysMatImgW; } if (ny > strSysMatImgH) { nx = strSysMatImgH * sx / sy; ny = strSysMatImgH; }} else { if (sy > strSysMatImgW) { nx = strSysMatImgH * sx / sy; ny = strSysMatImgH; } if (nx > strSysMatImgH) { ny = strSysMatImgW * sy / sx; nx = strSysMatImgW; }} 38

if (sx >= sy) { if (sx > strSysMatImgW) { ny = strSysMatImgW * sy / sx; nx = strSysMatImgW; } if (ny > strSysMatImgH) { nx = strSysMatImgH * sx / sy; ny = strSysMatImgH; }} else { if (sy > strSysMatImgH) { nx = strSysMatImgH * sx / sy; ny = strSysMatImgH; } if (nx > strSysMatImgW) { ny = strSysMatImgW * sy / sx; nx = strSysMatImgW; }} 39

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

Zasady Object Calisthenics

40

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

– Nazwy powinny być na tyle precyzyjne, by jednoznacznie dało się tylko po niej stwierdzić, za co

odpowiada klasa, metoda, bądź zmienna

Zasady Object Calisthenics

41

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

– Nazwy powinny być na tyle precyzyjne, by jednoznacznie dało się tylko po niej stwierdzić, za co

odpowiada klasa, metoda, bądź zmienna

– Gdy nie umiesz wymyślić adekwatnej nazwy klasy, zastanów się, czy ta klasa ma sens w ogóle

Zasady Object Calisthenics

42

● Czytelność i ułatwione zarządzanie kodem

● Okazja do wykrycia problemów z architekturą systemu

● Wykorzystanie wsparcia IDE do podpowiadania (i wyszukiwania) nazw

Zalety:

43

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

7. Klasy i metody muszą być małe

Zasady Object Calisthenics

44

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

7. Klasy i metody muszą być małe

– Maksymalnie 50 linii na metodę w klasie

Zasady Object Calisthenics

45

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

7. Klasy i metody muszą być małe

– Maksymalnie 50 linii na metodę w klasie

– Maksymalnie 10 metod w klasie

Zasady Object Calisthenics

46

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

7. Klasy i metody muszą być małe

– Maksymalnie 50 linii na metodę w klasie

– Maksymalnie 10 metod w klasie

– Maksymalnie 15 klas w pakiecie

Zasady Object Calisthenics

47

48

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

7. Klasy i metody muszą być małe

8. Ogranicz ilość pól w klasie (Eng. Instance Variables) do 2 kilku

Zasady Object Calisthenics

49

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

7. Klasy i metody muszą być małe

8. Ogranicz ilość pól w klasie (Eng. Instance Variables) do 2 kilku

Zasady Object Calisthenics

50

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

7. Klasy i metody muszą być małe

8. Ogranicz ilość pól w klasie (Eng. Instance Variables) do 2 kilku

Zasady Object Calisthenics

51

● Mniej zależności

● Opakowanie typów prostych

● Większa spójność

● Łatwiejsze mockowanie na potrzeby testów jednostkowych

Zalety:

52

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

7. Klasy i metody muszą być małe

8. Ogranicz ilość pól w klasie (Eng. Instance Variables) do 2 kilku

9. Mów, nie pytaj (Unikaj GET’erów oraz SET’erów)

Zasady Object Calisthenics

53

// Gameprivate int score;

public void setScore(int score) { this.score = score;}

public int getScore() { return score;}

// Usagegame.setScore(game.getScore() + ENEMY_DESTROYED_SCORE);

54

// Gameprivate int score;

public void addScore(int delta) { this.score += delta;}

// Usagegame.addScore(ENEMY_DESTROYED_SCORE);

55

// Gameprivate int score;

public void addScore(int delta) { this.score += delta;}

public int getScore() { return score;}

// Usagegame.addScore(ENEMY_DESTROYED_SCORE);

56

● Logika klasy wewnątrz klasy – nie jest rozproszona po całym projekcie

Zalety:

57

1. Tylko jeden poziom wcięcia na metodę

2. Unikaj używania ELSE

3. Opakowuj typy proste

4. Kolekcje jako osobne klasy

5. Jedna kropka na linię

6. Nie skracaj nazw

7. Klasy i metody muszą być małe

8. Ogranicz ilość pól w klasie (Eng. Instance Variables) do 2 kilku

9. Mów, nie pytaj

Zasady Object Calisthenics

58

Pytania ...

59

● Rafael Dohms, Your code sucks, let's fix it (PHP Benelux, Antwerpia 2013)

● Guilherme Blanco, Object Calisthenics Applied to PHP (GTA Meetup, Toronto 2012)

● Paweł Lewtak, Object calisthenics (PHPCon, Rawa Mazowiecka 2016)

● http://williamdurand.fr/2013/06/03/object-calisthenics/

● https://www.cs.helsinki.fi/u/luontola/tdd-2009/ext/ObjectCalisthenics.pdf

● http://williamdurand.fr/2012/01/24/designing-a-software-by-naming-things/

● http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/general-formulation.html

● http://wiki.c2.com/?LawOfDemeter

Bibliografia:

60

Dziękuję

61

Recommended