Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Strategie zmiany Legacy Code

Strategie zmiany Legacy Code

Tomasz Gramza

August 20, 2016
Tweet

More Decks by Tomasz Gramza

Other Decks in Programming

Transcript

  1. Strategie pracy z Legacy Code PHPers Summit 2016, Poznań, 20.08.2016

    r. Tomasz Gramza mail: [email protected] twitter: @tomaszgramza linkedin: @tomaszgramza github: @to-masz zmiany
  2. 2

  3. 4

  4. Agenda 5 Legacy Code praca z Strategie zmiana 1. 2.

    3. Co to? Skąd się bierze? Po co? Przykłady … Potrzeba zmiany Dobra zmiana :)
  5. 6

  6. 7

  7. 8

  8. 10

  9. Legacy Code - skojarzenia Nieprzejrzysta, splątana struktura 14 Brak testów

    Niezrozumiały Muszę z nim pracować, a nie mam ochoty Tydzień walki z dodaniem funkcjonalności, której zrobienie powinno zająć tylko chwilę
  10. 15

  11. 16 Legacy code - kod, z którego nie jesteśmy już

    zadowoleni - kod, który nam ciąży - kod, o którym chcielibyśmy zapomnieć
  12. 18

  13. 22 ID_LOKALIZACJA WOJEWÓDZTWO MIASTO DZIELNICA 1 WIELKOPOLSKIE POZNAŃ GRUNWALD 2

    WIELKOPOLSKIE POZNAŃ NOWE MIASTO ID_LOKALIZACJA ID_WOJEWÓDZTWO ID_MIASTO ID_DZIELNICA 1 14 220 34 2 14 220 35 WOJEWÓDZTWA MIASTA DZIELNICE Co jesli dwa miasta o tej samej nazwie?
  14. Ile Legacy Code mamy w projekcie? •  Zduplikowany kod • 

    Naruszenia reguł •  Nieudokumentowane publicze metody •  Pokrycie złożoności kodu testami poniżej 80% •  Nadmierna złożoność metod/klas 25 https://hub.docker.com/_/sonarqube/
  15. 26

  16. 27

  17. 28

  18. 29

  19. 31

  20. 32 1.  Jakie zmiany musimy wprowadzic? 2.  Skad bedziemy wiedziec,

    ze dokonalismy poprawnych zmian? 3.  Skad bedziemy wiedziec, ze nic nie popsulismy?
  21. Algorytmy zmiany kodu 1.  Znajdź miejsca zmiany 2.  Znajdź miejsca

    do przetestowania 3. Spraw aby kod był testowalny 4. Napisz testy 5. Dokonaj zmiany 33 1.  Dokonaj zmiany 2.  Módl się vs. 1.  Jakie zmiany musimy wprowadzic? 2.  Skad bedziemy wiedziec, ze dokonalismy poprawnych zmian? 3.  Skad bedziemy wiedziec, ze nic nie popsulismy?
  22. + Dobra zmiana 34 Do it right the second time

    •  Dodanie funkcjonalności •  Naprawa błędu Refaktoryzacja
  23. Refaktoryzacja - Przybornik •  VCS •  PHP 7 •  IDE

    •  Wytyczne do formatowania kodu: PSR 1/2 + php-cs-fixer •  Composer (autoloader) 35
  24. Refaktoryzacja - Brak globalnych zależności •  Brak zmiennych globalnych global

    $user; •  Brak super globalnych zmiennych $_GET, $_SESSION, $COOKIE 36
  25. class Foo { public function __construct() { } public function

    getBar() { global $user; return $user[‘name’]; } } 37 class Foo { private $user; public function __construct($user) { $this->user = $user; } public function getBar() { return $this->user[‘name’]; } }
  26. class Foo { public function __construct() { } public function

    getBar() { return $_COOKIE[‘user’]; } } 38 class Foo { private $request; public function __construct( ServerRequestInterface $request ) { $this->request = $request; } public function getBar() { return $this->request ->getCookieParams()[‘user’]; } }
  27. Refaktoryzacja - Odwrócenie sterowania (Dependency Injection) 39 •  Usługi • 

    Fabryki Obiekt może tworzyć inne obiekty albo operować na nich. Nigdy jedno i drugie na raz!
  28. class Foo { public function __construct() { } public function

    bar() { $stats = new Statistics(); $stats->process(); } } 40 class Foo { private $statistics; public function __construct( Statistics $stats ) { $this->statistics = $stats; } public function bar() { return $this->statistics ->process(); } }
  29. class Foo { public function __construct() { } public function

    bar($data) { $collection = []; foreach ($data as $row) { $collection[] = new Item($row); } return $collection; } } 41 class Foo { private $factory; public function __construct( ItemFactory $factory ) { $this->factory = $factory; } public function bar($data) { $collection = []; foreach ($data as $row) { $collection[] = $this->factory ->newInstance($row); } return $collection; } }
  30. Refaktoryzacja - automatyzacja testów 42 •  Jeśli możesz napisać test,

    który sprawdza tylko wynik działania metody, powinieneś to zrobić •  Jeśli możesz napisać test funkcjonalny albo integracyjny, który oddziałuje na baze danych, połączyenie sieciowe, czy system plików, powinieneś to zrobić •  Jeśli możesz napisać luźny test jednostkowy, który testuje konkretne klasy (bez pełnej izolacji), powinieneś to zrobić •  Jeśli możesz napisać prawidłowy test, który wykorzystuje imitacje testowe (test doubles), żeby w pełni odizolować testowaną klasę, powinieneś to zrobić An imperfect test today is better than a perfect test someday.
  31. class Foo { private $db; public function __construct( Db $db

    ) { $this->db = $db; } public function getBar($id) { $sql = “SELECT * FROM bar WHERE id = :id”: return $db->fetchRow($sql, [‘:id’ => $id]); } } 44 class Foo { private $gateway; public function __construct( BarGateway $gateway ) { $this->gateway = $gateway; } public function getBar($id) { return $this->gateway ->findById($id); } } ...
  32. class Foo { private $db; public function __construct( Db $db

    ) { $this->db = $db; } public function getBar($id) { $sql = “SELECT * FROM bar WHERE id = :id”: return $db->fetchRow($sql, [‘:id’ => $id]); } } 45 ... class BarGateway { private $db; public function __construct( Db $db ) { $this->db = $db; } public function findById($id) { $sql = “SELECT * FROM bar WHERE id = :id”: return $db->fetchRow($sql, [‘:id’ => $id]); } }
  33. <?php if($location- >hasDistrict()): ?> <?=$location->getCityName()?>. <?=$location->getDistrictName()?> <?php else: ?> <?=$location->getCityName()?>

    <?php endif; ?> 47 class StandardLocationPresenter { private $location; public function __construct( Location $location ) { $this->location = $location; } public function getName(): string { ... } } ... <?=$locationPresenter->getName()?>
  34. 49

  35. Strategia Refaktoryzuj do Open-Closed 51 Robot +fire() +getWeapon(): Cannon ...

    RobotWithBfg +fire() +getWeapon(): BFG9000 ... $robot = new RobotWithBfg(); $robot->fire();
  36. Strategia Refaktoryzuj do Open-Closed 52 Robot +fire() +getWeapon(): Cannon ...

    RobotWithBfg +fire() +getWeapon(): BFG9000 ... $robot = new RobotWithBfg(); $robot->fire();
  37. Strategia Refaktoryzuj do Open-Closed 53 Robot +fire() ... Cannon +fire()

    +getWeapon(): Cannon ... $robot = new Robot(); $robot->fire();
  38. Strategia Refaktoryzuj do Open-Closed 54 Robot +fire() ... Cannon +fire()

    ... $robot = new Robot(); $robot->fire(); Weapon +fire() +getWeapon(): Weapon ...
  39. Strategia Refaktoryzuj do Open-Closed 55 Robot +fire() ... Cannon +fire()

    ... $robot = new Robot(); $robot->fire(); Weapon +fire() +getWeapon(): Weapon ... BFG900 +fire() ...
  40. Strategia Refaktoryzuj do Open-Closed 56 Robot +fire() ... Cannon +fire()

    ... $weapon = new BFG9000(); $robot = new Robot($weapon); $robot->fire(); Weapon +fire() +getWeapon(): Weapon ... BFG900 +fire() ...
  41. Strategia Duszenie systemu 58 BookingService +book() ... $service = new

    BookingService(); $service>book(); BookingServiceV2 +bookItem() +cancelItemBooking() ...
  42. Strategia Duszenie systemu 59 BookingService +book() ... $service = new

    BookingService(); $service>book(); $service = new BookingServiceV2( new BookingService(); ); $service>bookItem(); class BookingServiceV2 { ... public function bookItem() { $this->legacyService->book(); } } BookingServiceV2 +bookItem() +cancelItemBooking() ...
  43. Strategia Duszenie systemu 60 BookingService +book() ... $service = new

    BookingServiceV2(); $service>bookItem(); class BookingServiceV2 { ... public function bookItem() { // new implementation } } BookingServiceV2 +bookItem() +cancelItemBooking() ...
  44. Strategia Duszenie systemu 61 $service = new BookingServiceV2(); $service>bookItem(); class

    BookingServiceV2 { ... public function bookItem() { // new implementation } } BookingServiceV2 +bookItem() +cancelItemBooking() ...
  45. Strategia Flaga funkcjonalności 63 BookingService +book() ... $isNewBookingFeatureActive; $service =

    new BookingService( $isNewBookingFeatureActive ); $service>book(); class BookingService { ... public function book() { if ($this ->isNewBookingFeatureActive) { return ... } ... } }
  46. Legacy Code praca z Strategie Podsumowanie 64 Da się z

    nim żyć! Nie działaj chaotycznie! - Korzystaj ze strategii Do it right the second time! - jesli dostarczasz zbyt szybko - zaplanuj refaktoryzację! zmiana 1. 2. 3.