2. O mnie
GOG.com
Senior Web Developer
Czym się zajmuję?
Rozwijam i utrzymuję kod
W międzyczasie szukam złotego Graala
designu aplikacji ;)
3. GOG.com
Historia Start w 2008 pod skrzydłami CD Projekt - zespół 10 osób
Obecnie zatrudnionych jest ponad 70 osób
Obecna
pozycja
#1 globalnej dystrybucji klasycznych gier na PC i Mac
#2 globalnej dystrybucji gier indie na PC i Mac
Partnerzy 220+ twórców i wydawców gier
Klienci Ponad 2.7 miliona unikalnych wejść miesięcznie z całego
świata
Gry Ponad 800 tytułów w katalogu
Ponad 39 milionów gier na kontach użytkowników
4. Agenda
1. Czym jest złożoność?
2. Dlaczego złożoność jest problematyczna?
3. Złożoność a design kodu
4. Złożoność poznawcza
5. Podsumowanie i wnioski
5. Czym jest złożoność?
CZYM JEST ZŁOŻONOŚĆ?
• Trudność w zrozumieniu, wykonaniu i zweryfikowaniu poprawności
projektu.
• Im większa liczba możliwych interakcji w procesie, tym więcej
rzeczy może pójść nie tak.
6. Dlaczego złożoność jest problematyczna?
CZŁOWIEK TO NIE MASZYNA
• Ludzki umysł nie może ogarnąć nieskończonej ilości informacji.
• Według badań, człowiek może myśleć o maksymalnie 4 rzeczach na raz.
• Zbyt dużo informacji na raz to ryzyko nieświadomej pomyłki.
7. Dlaczego złożoność jest problematyczna?
TOKSYCZNY WPŁYW NA PROJEKT
• Złożoność rodzi złożoność.
• Zamiast myśleć o istocie problemu i sposobie rozwiązania musimy
myśleć o stanie obecnym i o tym jak zaadaptować się do nowych
warunków.
8. Złożoność a design kodu
PODSTAWOWE METRYKI
Cyclomatic Complexity
(CC)
Liczba linearnie niezależnych ścieżek, w kodzie z
jednym punktem wyjścia równa jest liczbie punktów
decyzyjnychNPath Complexity (NP) Liczba możliwych różnych przebiegów w kodzie
Afferent Coupling (CA) Liczba klas, które polegają na danej implementacji
Efferent Coupling (CE) Liczba klas, na których polega dana klasa
Stability (I) Wartość: CE / (CA + CE), określa ogólną stabilność,
0 to całkowita stabilność, 1 to całkowita niestabilność
9. Złożoność a design kodu
function fizzBuzz($n)
{
$output = "";
if ($output % 3 == 0) {
$output .= "Fizz";
}
if ($output % 5 == 0) {
$output .= "Buzz";
}
return $output;
}
10. Złożoność a design kodu
REKOMENDOWANE LIMITY (wg PMD i
phpMD)
• CC <= 10
• NPath <= 200
• Powyżej podanych metryk, kod przeważnie zaczyna robić się trudny
do zrefaktorowania i zrozumienia.
http://phpmd.org/rules/index.html#code-size-rules
11. Złożoność a design kodu
TESTY JEDNOSTKOWE
• Chronią przed niepożądanymi zmianami.
• Pozwalają na bardziej swobodną refaktoryzację.
12. Złożoność a design kodu
• Kod ma 100% code coverage, 100% path coverage.
• Jesteśmy kryci przed każdą pomyłką?
• .. niestety nie. Ale prawie.
$this->assertEquals(fizzBuzz(2) == "");
$this->assertEquals(fizzBuzz(9) == "Fizz");
$this->assertEquals(fizzBuzz(10) == "Buzz");
$this->assertEquals(fizzBuzz(30) == "FizzBuzz");
13. Złożoność a design kodu
ROZBIJANIE PROBLEMÓW:
SEPARATION OF CONCERNS
• Nasze aplikacje rozwiązują jakiś (duży) problem, najczęściej biznesowy.
• Każdy problem można rozbić na więcej pod-problemów.
14. Złożoność a design kodu
OBSŁUŻENIE ŻĄDANIA HTTP KLIENTA
PRZESŁANIE OBIEKTU
REQUEST DO KONTROLERA
URUCHOMIENIE BUNDLI
STWORZENIE OBIEKTU
REQUEST
WYBRANIE ROUTE’A NA
PODSTAWIE ŚCIEŻKI
ŻĄDANIA HTTP
ODESŁANIE ODPOWIEDZI
WYTWORZENIE OBIEKTU TYPU RESPONSE
ODPYTANIE BAZY DANYCH
O ŻĄDANE INFORMACJE
GENERACJA WIDOKU
DLA UŻYTKOWNIKA
URUCHOMIENIE INNYCH
ŻĄDANYCH PROCESÓW
ZAPYTANIE DO
BAZY DANYCH
GENERACJA
SZABLONÓW TWIG
ZAPYTANIE
DO CACHE
OTRZYMANIE OBIEKTU TYPU RESPONSE
15. Złożoność a design kodu
OBSŁUŻENIE ŻĄDANIA HTTP KLIENTA
URUCHOMIENIE BUNDLI
STWORZENIE OBIEKTU
REQUEST
WYBRANIE ROUTE’A NA
PODSTAWIE ŚCIEŻKI
ŻĄDANIA HTTP
PRZESŁANIE OBIEKTU
REQUEST DO KONTROLERA
WYTWORZENIE OBIEKTU TYPU RESPONSE
ODPYTANIE BAZY DANYCH
O ŻĄDANE INFORMACJE
GENERACJA WIDOKU
DLA UŻYTKOWNIKA
URUCHOMIENIE INNYCH
ŻĄDANYCH PROCESÓW
ZAPYTANIE DO
BAZY DANYCH
GENERACJA
SZABLONÓW TWIG
ZAPYTANIE
DO CACHE
16. Złożoność a design kodu
OBSŁUŻENIE ŻĄDANIA HTTP KLIENTA
URUCHOMIENIE BUNDLI
STWORZENIE OBIEKTU
REQUEST
WYBRANIE ROUTE’A NA
PODSTAWIE ŚCIEŻKI
ŻĄDANIA HTTP
PRZESŁANIE OBIEKTU
REQUEST DO KONTROLERA
WYTWORZENIE OBIEKTU TYPU RESPONSE
URUCHOMIENIE INNYCH
ŻĄDANYCH PROCESÓW
GENERACJA WIDOKU
DLA UŻYTKOWNIKA
GENERACJA
SZABLONÓW TWIG
ODPYTANIE BAZY DANYCH
O ŻĄDANE INFORMACJE
ZAPYTANIE DO
BAZY DANYCH
ZAPYTANIE
DO CACHE
17. Złożoność a design kodu
OBSŁUŻENIE ŻĄDANIA HTTP KLIENTA
PRZESŁANIE OBIEKTU
REQUEST DO KONTROLERA
URUCHOMIENIE BUNDLI
STWORZENIE OBIEKTU
REQUEST
WYBRANIE ROUTE’A NA
PODSTAWIE ŚCIEŻKI
ŻĄDANIA HTTP
WYTWORZENIE OBIEKTU TYPU RESPONSE
URUCHOMIENIE INNYCH
ŻĄDANYCH PROCESÓW
GENERACJA WIDOKU
DLA UŻYTKOWNIKA
GENERACJA
SZABLONÓW TWIG
ODPYTANIE BAZY DANYCH
O ŻĄDANE INFORMACJE
ZAPYTANIE DO
BAZY DANYCH
ZAPYTANIE
DO CACHE
ODESŁANIE ODPOWIEDZI
OTRZYMANIE OBIEKTU TYPU RESPONSE
18. Złożoność a design kodu
DOBRE OBIEKTY TO MAŁE OBIEKTY
• Uniwersalna zasada: „preferuj gęstszą sieć mniejszych obiektów od rzadszej
sieci większych obiektów” - Nigel Thorne
• Single Responsibility Principle
• Design By Contract (Liskov Substitute Principle)
• Dependency Inversion Principle
• Interface Segregation Principle
• Tell, don’t ask
http://stackoverflow.com/questions/243274/best-practice-with-unit-testing-abstract-classes
19. Złożoność a design kodu
SINGLE RESPONSIBILITY PRINCIPLE
• Każdy obiekt powinien mieć jedną odpowiedzialność.
• “Odpowiedzialność to powód do modyfikacji” - Robert C. Martin
20. Złożoność a design kodu
class AppKernel { … }
class Controller { … }
class TwigEngine implements EngineInterface { … }
21. Złożoność a design kodu
DESIGN BY CONTRACT
• Wprowadzenie jasnego kontraktu umożliwia jawne przekazanie informacji.
• Te informacje to m.in to, czego dana klasa oczekuje, co i w jakim wypadku zwraca,
a także wyjątki które może zwrócić i co one oznaczają.
• Brak typów wartościowych (int, string…) oraz zwracanych wartości w PHP niestety
to utrudniają, ale annotacje PHPDoc (@param, @return, @throws) i dobre IDE
pomagają w utrzymaniu dyscypliny.
22. Złożoność a design kodu
interface UserRepository {
public function findOne(UserId $userId);
}
class MysqlUserRepository {
private $connection;
public function __construct(Connection $connection) {
$this->connection = $connection;
}
public function findOne(UserId $userId) {
return $this->connection->query("SELECT * from users WHERE id = :uid", [$userId->value()]);
}
}
class InMemoryUserRepository { […] }
23. Złożoność a design kodu
DEPENDENCY INVERSION PRINCIPLE
• Nie powinno być „hierarchii klasowej”. Zamiast tego, wszystko powinno być
zależne od abstrakcyjnych interfejsów.
• Abstrakcja nie powinna polegać na implementacji (to ma być złączone z
poniższym).
• Implementacja powinna polegać na abstrakcji.
24. Złożoność a design kodu
class BlogController {
public function __construct(BlogPagesRepository $repository ) { … }
}
interface BlogPagesRepository { … }
class MysqlBlogPagesRepository implements BlogPagesRepository { … }
class MongoDbPagesRepository implements BlogPagesRepository { … }
25. Złożoność a design kodu
INTERFACE SEGREGATION PRINCIPLE
• Klient nie powinien zależeć od metod, którego nie potrzebuje.
• Ułatwia tworzenie implementacji do granularnych interfejsów.
26. Złożoność a design kodu
interface VisitorInterface {
public function getIP();
}
interface UserInterface {
public function getId();
}
class HttpUser implements VisitorInterface, UserInterface { … }
class Crawler implements VisitorInterface { … }
class CliUser implements UserInterface { … }
27. Złożoność a design kodu
TELL, DON’T ASK
• Mów obiektom co mają robić, nie pytaj ich o stan.
• Stawiaj warunki tam, gdzie są one zasadne dla danego problemu.
28. Złożoność a design kodu
// BasketController.php
if($customer->getCountOfItemsInBasket() < 3) {
$customer->addToBasket($item);
}
ŹLE (IMPLEMENTACJA KLASY CUSTOMER WYCIEKA DO KONTROLERA):
29. Złożoność a design kodu
//BasketController.php
$customer->addToBasket($item);
//Customer.php
public function addToBasket($item) {
if($this->getCountOfItemsInBasket() < 3) {
[…]
}
}
DOBRZE:
30. Złożoność a design kodu
KORZYŚCI ZE STOSOWANIA
• Odizolowanie problemów od siebie = Większa elastyczność
• Mniejsze problemy = Mniejsza złożoność
31. Złożoność a design kodu
SCOPE
• Każdy kod posiada jakiś scope, w którym ma
zadeklarowane swoje wartości.
• Wszystkie dane w scopie powinny być mu jawnie
przekazane poprzez parametry.
32. Złożoność a design kodu
PROBLEMY ZE SCOPE’EM
• Global variable: global $var;
• Mutability: setFoo($foo) - Dla obiektów które nie przenoszą danych
• Zależność od implementacji
33. Złożoność a design kodu
POMOCNE NARZĘDZIA
phpMD Na podstawie reguł alarmuje Nas, gdy dana klasa/
metoda może okazać się problematyczna w utrzymaniu
PHP Code Sniffer Sprawdza zgodność z standardami PSR
PHP Code Analyzer Wykonuje analizę statyczną kodu, inferencję typów ,
wykrywa proste błędy
Solidne IDE Wspomaga pisanie i formatowanie kodu, minimalizuje
ryzyko wystąpienia pomyłki mechanicznej ( syntax error,
zły typ, zła klasa, zła metoda.. etc. )
34. Złożoność poznawcza kodu
ZŁOŻONOŚĆ KOGNITYWNA
• Jawność tego, co dzieje się w kodzie dla osoby czytającej
• Często pomijana (ze względu na swoją
„nietechniczność”), ale jest nie mniej ważna od złożoności
samego kodu
35. Złożoność poznawcza kodu
ODBIÓR JEST WSZYSTKIM
• Czytelność kodu (Coding Style)
• Język wykorzystywany w aplikacji
• Domain Driven Design, Ubiquitous Language - metodologia,
która minimalizuje narzut w odbiorze
36. Podsumowanie i wnioski
WNIOSKI
• Zdanie sobie sprawę z tego, co dokładnie sprawia, że kod ciężej
utrzymać to pół sukcesu.
• Pisanie łatwego kodu jest trudne - przepisanie trudnego kodu może być
jeszcze trudniejsze.
• Sprawdzone rozwiązania z zakresu projektowania aplikacji pomagają w
ograniczeniu złożoności aplikacji do koniecznego minimum.
• Czytelność i nazewnictwo są tak samo ważne co sam kod.
37. Podsumowanie i wnioski
CO ZYSKUJE DEVELOPER I PRODUCT OWNER?
• Czas
• Elastyczność w adaptacji do nowych wymogów biznesowych
• Elastyczność w tworzeniu technicznych implementacji
• Podatność na zmiany, odporność na uszkodzenia produktu