SlideShare ist ein Scribd-Unternehmen logo
REFAKTORYZACJA
Z praktycznego punktu
widzenia.
O mnie
• Marek Keżel
• 7 lat doświadczenia w PHP (5.6, 7, 8, 9, 10, 11, 12 J)
• Programista w Innovation Software Sp. z o. o.
• Niezależny konsultant ds. oprogramowania w Specific Sp. z o. o.
• NDProdukcja – twórca wewnętrznego systemu ERP jednego z
największych producentów mebli tapicerowanych w Polsce
• Mąż i Tata
Agenda
•Czym jest refaktoryzacja
•Szybki przegląd technik refaktoryzacji
•Omówienie 8 wybranych technik
•Czemu nie zawsze będziesz refaktoryzował kod
•Pytania, sugestie, ewentualne wygwizdanie prelegenta
CZYM JEST
REFAKTORYZACJA
Czym jest refaktoryzacja ?
Refaktoryzacja to wymyślony proces przez rozpieszczonych
programistów, którzy myślą, że głupi ludzie zapłacą im ponownie za tą
samą pracę albo co gorsze za udawanie, że coś robią.
Janusz, dyrektor operacyjny, członek zarządu
Czym jest refaktoryzacja ?
The main purpose of refactoring is to fight technical debt. It transforms
a mess into clean code and simple design.
https://refactoring.guru/refactoring/what-is-refactoring
In computer programming and software design, code refactoring is the
process of restructuring existing computer code—changing the
factoring—without changing its external behavior. Refactoring is
intended to improve the design, structure, and/or implementation of
the software (its non-functional attributes), while preserving its
functionality.
https://en.wikipedia.org/wiki/Code_refactoring
Czym jest refaktoryzacja ?
Głównym celem przeprowadzania refaktoryzacji jest walka z długiem
technologicznym*, przeistoczenie brzydkiego kodu w czysty* ZROZUMIAŁY DLA
INNYCH.
*Dług technologiczny (Technical Debt) jest naszym mniej lub bardziej świadomym
działaniem, które ciągnie nasze oprogramowanie w dół. Ignorujemy dobre praktyki
w imię wyższych celów (tak nam się wydaje…) jak np. szybszy release przez co
pomijamy pisanie testów, nie kontrolujemy zależności, tworzymy niezrozumiałe
nazwy pól oraz metod. Czasami jesteśmy „dłużni” ponieważ robimy to
nieświadomie – nie mamy wiedzy jak coś zrobić lepiej. Czym się objawia dług?
Wydłużonym czasem wprowadzania zmian w kodzie, większą ilość błędów, coraz
większą ilością próśb o przeniesienie do innego projektu.
*Czysty kod (Clean Code) to taki który jest łatwy w utrzymaniu, zrozumiały dla
innych programistów (bez żadnej magii, dziwacznego nazewnictwa, rozdętych
metod oraz klas), bez zduplikowanego kodu, pozbawiony tzw. zapaszków (Code
Smells). Czysty kod czyta się jak dobrą książkę z uśmiechem a nie z przerażeniem w
stylu „co to $%^& jest !?”.
Czym jest refaktoryzacja ?
Kiedy wykonywać refaktoryzację ?
• Kiedy patrzymy na kod i czujemy, że skręca nas w środku na
sam widok – to jest idealny moment!
• Kiedy dodajemy nową funkcjonalność do naszego programu
(obie czynności traktujemy oddzielnie!)
• Kiedy naprawiamy jakiś problem
• Kiedy robimy codereview
• Kiedy robimy coś i mamy Déjà vu, że coś takiego już robiliśmy
Kiedy NIE wykonywać refaktoryzacji ?
• Kiedy nie rozumiemy kodu nad którym pracujemy
• Kiedy kod już działa produkcyjnie i nie ma z nim żadnych
problemów
• Kiedy projekt jest w stylu napisz i zapomnij
• Kiedy nie mamy pokrycia w testach tego co chcemy
zrefaktoryzować*
*dopiszemy te testy lub każdy z nas podejmie super ważną decyzje mając z
tyłu głowy złotą zasadę (przetestujemy na produkcji) J
Jak wykonać refaktoryzację ?
„Refaktoryzacja powinna być wykonana jako seria małych zmian które czynią
nasz kod lepszym, pozostawiając jednocześnie program w stanie gotowości”
• Jeżeli kod po wykonaniu refaktoryzacji dalej jest „brzydki” wracamy do
początku i robimy to jeszcze raz.
• Podczas refaktoryzacji nie powinny powstawać żadne nowe funkcjonalności
(proces refaktoryzacji powinien być oddzielny)
• Wszystkie istniejące testy muszą zostać poprawnie wykonane (czasami to
testy są winne niepowodzeń np. są zbyt nisko poziomowe, przez co musimy
dokonać refaktoryzacji samych testów)
https://refactoring.guru/refactoring/how-to
SZYBKI PRZEGLĄD
TECHNIK
REFAKTORYZACJI
Na podstawie: https://refactoring.guru/refactoring/techniques
Kompozycja metod
• Extract Method
• Inline Method
• Extract Variable
• Inline Temp
• Replace Temp with Query
• Split Temporary Variable
• Remove Assignments to
Parameters
• Replace Method with Method
Object
• Substitute Algorithm
Przenoszenie funkcjonalności
• Move Method
• Move Field
• Extract Class
• Inline Class
• Hide Delegate
• Remove Middle Man
• Introduce Foreign Method
• Introduce Local Extension
Ogranizacja danych
• Change Value to Reference
• Change Reference to Value
• Duplicate Observed Data
• Self Encapsulate Field
• Replace Data Value with Object
• Replace Array with Object
• Change Unidirectional
Association to Bidirectional
• Change Bidirectional Association
to Unidirectional
• Encapsulate Field
• Encapsulate Collection
• Replace Magic Number with
Symbolic Constant
• Replace Type Code with Class
• Replace Type Code with
Subclasses
• Replace Type Code with
State/Strategy
• Replace Subclass with Fields
Upraszczanie wyrażeń regulanych
• Consolidate Conditional
Expression
• Consolidate Duplicate
Conditional Fragments
• Decompose Conditional
• Replace Conditional with
Polymorphism
• Remove Control Flag
• Replace Nested Conditional with
Guard Clauses
• Introduce Null Object
• Introduce Assertion
Upraszczanie wywołań metod / funkcji
• Add Parameter
• Remove Parameter
• Rename Method
• Separate Query from Modifier
• Parameterize Method
• Introduce Parameter Object
• Preserve Whole Object
• Remove Setting Method
• Replace Parameter with Explicit
Methods
• Replace Parameter with Method
Call
• Hide Method
• Replace Constructor with Factory
Method
• Replace Error Code with
Exception
• Replace Exception with Test
Upraszczanie abstrakcji
• Pull Up Field
• Pull Up Method
• Pull Up Constructor Body
• Push Down Field
• Push Down Method
• Extract Subclass
• Extract Superclass
• Extract Interface
• Collapse Hierarchy
• Form Template Method
• Replace Inheritance with
Delegation
• Replace Delegation with
Inheritance
OMÓWIENIE
WYBRANYCH TECHNIK
UWAGA!
W celu uproszczenia a zarazem
zmieszczenia się w limicie
czasowym, nie wszystkie drobne
kroki refaktoryzacji zostaną
pokazane na slajdach.*
*Ale prowadzący o nich wspomni :)
EXTRACT METHOD
Extract Method
Kiedy? Metoda ma za dużo odpowiedzialności (narusza SRP). Czujemy,
że coś nam nie pasuje.
Co zyskamy? Ograniczymy odpowiedzialność metody, zwiększymy
czytelność kodu.
Jak to zrobić? Tworzymy nową metodę (modyfikator dostępu?),
przenosimy fragment kodu do nowej metody, jeżeli kod korzysta ze
zmiennych zadeklarowanych wcześniej – przekazujemy je, w bazowej
metodzie odwołujemy się do nowo utworzonej, wykonujemy testy
Metoda odwrotna? Inline Method
Extract Method - przykład
class OptymalizerkaBoki
{
public function wyslijFormatkiNaOptymalizerke(array $formatki, int $optymalizerkaID): void
{
$formatkiWezglowia = [];
foreach ($formatki as $formatka) {
if ($formatka->czyWezglowie() === false) {
continue;
}
$formatkiWezglowia[] = $formatka;
}
$optymalizerkaWiadomosc = new OptymalizerkaWiadomosc();
$optymalizerkaWiadomosc->setFormatki($formatkiWezglowia);
$this->getBus($optymalizerkaID)->wyslij($optymalizerkaWiadomosc);
}
}
Extract Method - przykład
class OptymalizerkaBokiRefactoryzacja
{
public function wyslijFormatkiNaOptymalizerke(array $formatki, int $optymalizerkaID): void
{
$formatkiWezglowia = $this->wyfiltrujFormatkiWezglowia($formatki);
$optymalizerkaWiadomosc = new OptymalizerkaWiadomosc();
$optymalizerkaWiadomosc->setFormatki($formatkiWezglowia);
$this->getBus($optymalizerkaID)->wyslij($optymalizerkaWiadomosc);
}
private function wyfiltrujFormatkiWezglowia(array $formatki): array
{
$formatkiWezglowia = [];
foreach ($formatki as $formatka) {
if ($formatka->czyWezglowie() === false) {
continue;
}
$formatkiWezglowia[] = $formatka;
}
return $formatkiWezglowia;
}
}
EXTRACT CLASS
Exctract Class
Kiedy? Klasa posiada wiele odpowiedzialności, robi za dużo, narusza zasadę
SRP (Single Responsibility Principle), jest mniej czytelna i trudna w analizie
Co zyskamy? Zbliżymy się albo osiągniemy zgodność z zasada SRP,
zwiększymy tolerancję na zmiany, poprawimy czytelność oraz zrozumienie
kodu
Jak to zrobić? Tworzymy nową klasę oraz tworzymy relację między klasami
(najlepiej jednokierunkową) przenosimy do niej docelową funkcjonalność
(korzystamy z technik Move Field i Move Method) uważamy na
modyfikatory dostępu (zaczynamy od prywatnych), wykonujemy testy
Wady? Zbyt często wykorzystywana, powoduje mnóstwo nowych klas często
małych i nie koniecznie z odpowiednimi odpowiedzialnościami
Metoda odwrotna? Inline Class
Extract Class - przykład
class SaturnService
{
public function __construct()
{
$this->client = new SoapClient(self::SATURN_API_WSDL, $this->options);
}
public function potwierdzZamowienieDoDostawcy(Zamowienie $zamowienie): void
{
// coś robimy z zamówieniem oraz pobieramy docID
$response = $this->potwierdzZamowienieWyslijRequest($saturnDocID);
//coś robimy z odpowiedzą dalsza cześc logiki
}
private function potwierdzZamowienieWyslijRequest(string $saturnDocID): array
{
$result = $this->client->ZdConfirm(['ZdConfirm' => ['DocID' => $saturnDocID]]);
// cos robimy z rezultatem
return ['stauts' => $result->status, 'confirmCode' => $result->confirmCode];
}
}
Extract Class - przykład
class SaturnApi implements SaturnApiInterface
{
public function __construct()
{
$this->soapClient = new SoapClient(self::SATURN_API_WSDL, $this->options);
}
public function potwierdzZamowienieWyslijRequest(string $saturnDocID): array
{
$result = $this->soapClient->ZdConfirm(['ZdConfirm' => ['DocID' => $saturnDocID]]);
// cos robimy z rezultatem
return ['stauts' => $result->status, 'confirmCode' => $result->confirmCode];
}
}
Extract Class - przykład
class SaturnService
{
public function __construct(SaturnApiInterface $api)
{
$this->apiClient = $api;
}
public function potwierdzZamowienieDoDostawcy(Zamowienie $zamowienie): void
{
// cos robimy z zamówieniem oraz pobieramy docID
$saturnDocID = '';
$response = $this->apiClient->potwierdzZamowienieWyslijRequest($saturnDocID);
//coś robimy z odpowiedzą dalsza cześc logiki
}
}
REPLACE ARRAY WITH
OBJECT
Replace Array With Object
Kiedy? Jak z danymi w tablicy musimy zrobić coś więcej niż tylko je
wyświetlić.
Co zyskamy? Ułatwimy pracę ze strukturą danych (dopilnujemy
właściwych danych we właściwych miejscach). Uzyskamy enkapsulację
– połączymy dane z funkcjonalnością – OOP.
Jak to zrobić? Tworzymy strukturę nowej klasy w oparciu o źródłową
tablicę, zmieniamy w kodzie każde użycie tablicy na nasz nowy obiekt
(jeżeli nie możemy, to musimy zaimplementować funkcjonalność tablicy
w naszej nowej klasie)
Wady? Obiekty są „wolniejsze” zajmują więcej pamięci*.
*https://stackoverflow.com/questions/3709578/using-arrays-vs-objects-for-storing-data
Replace Array With Object - przykład
$wyliczonaFromatka = [
'id' => '1',
'typ' => 'WZG',
'nazwa' => 'Wezglowie_Panel_X',
'szerokosc' => 1000,
'wysokosc' => 1000,
'grubosc' => 3,
'ilosc' => 12,
'surowiecID' => 123,
'surowiecNazwa' => 'Plyta HDF',
];
Replace Array With Object - przykład
class CentrumService
{
public function wyslijFormatkeDoCentrum(array $formatka): void
{
//czy struktura tablicy $formatka jest poprawna ?
$kubatura = $this->wyliczKubature($formatka);
$polePowierzchni = $this->wyliczPolePowierzchni($formatka);
...
}
public function wyliczKubature(array $formatki): float
public function wyliczPolePowierzchni(array $formatki): float
}
Replace Array With Object - przykład
class WyliczonaFormatka
{
private int $id;
private string $typ;
private string $nazwa;
…
public static function utworzZTablicy(array $formatka): self
{
//sprawdzenie poprawnosci otrzymanej tablicy
return new self($formatka['id'], $formatka['typ'], $formatka['nazwa']);
}
public function __construct(int $id, string $typ, string $nazwa)
public function getKubatura(): float
public function getPolePowierzchni(): float
}
Replace Array With Object - przykład
class CentrumService
{
public function wyslijFormatkeDoCentrum(WyliczonaFormatka
$wyliczonaFormatka): void
{
$kubatura = $wyliczonaFormatka->getKubatura();
$polePowierzchni = $wyliczonaFormatka->getPolePowierzchni();
...
}
}
CONSOLIDATE
DUPLICATE
CONDITIONAL
FRAGMENTS
Consolidate Duplicate Conditional Fragments
Kiedy? Posiadamy zduplikowany kod w różnych gałęziach warunkowych
Co zyskamy? Usuniemy zduplikowany kod J
Jak to zrobić? Przenosimy zduplikowany kod przed lub za (zależnie od
jego lokalizacji) instrukcje warunkowe
Consolidate Duplicate Conditional Fragments - przykład
public function actionCreate()
{
$zamowienie = new Zamowienie();
$zamowienie->load($this->request->post('zamowienie'));
if ($this->request->post('lozko')) {
$zamowienie->load($this->request->post('lozko'));
$zamowienie->save();
return $this->redirect(['index']);
} elseif ($this->request->post('szafka')) {
$zamowienie->load($this->request->post('szafka'));
$zamowienie->save();
return $this->redirect(['index']);
} else {
$zamowienie->save();
return $this->redirect(['index']);
}
}
Consolidate Duplicate Conditional Fragments - przykład
public function actionCreate()
{
$zamowienie = new Zamowienie();
$zamowienie->load($this->request->post('zamowienie'));
if ($this->request->post('lozko')) {
$zamowienie->load($this->request->post('lozko'));
} elseif ($this->request->post('szafka')) {
$zamowienie->load($this->request->post('szafka'));
}
$zamowienie->save();
return $this->redirect(['index']);
}
REPLACE NESTED
CONDITIONAL WITH
GUARD CLAUSES
Replace Nested Conditional With Guard Clauses
Kiedy? Mamy mocno zagnieżdżone instrukcje warunkowe, które tworzą
kształt grotu strzały. Ciężko jest określić który warunek gdzie się kończy i
co robi
Co zyskamy? „Wyprostujemy” nasz kod, zlikwidujemy głębokie
zagnieżdżenia przez co zwiększymy czytelność kodu
Jak to zrobić? Wprowadzamy w każdej instrukcji warunkowej tzw.
Klauzule Ochronne (Guard Clauses), które automatycznie zwracają
jakąś wartość lub przerywają działanie programu wyjątkiem
Replace Nested Conditional With Guard Clauses - przykład
if ($zamowienie->czyZakonczone()) {
if ($zamowienie->getFirma()->nazwa === 'Januszex 2022 Sp. z. o.') {
$tranposrt = $this->spedytor->zarezerwujKubature($zamowienie->getKubatura());
$promocyjnaCena = $this->cennik->standardowaCena() * 5;
$faktura = $this->rozliczenia->wygenerujFakture($zamowienie);
$this->printer->drukuj($faktura);
} else {
$cena = $this->cennik->standardowaCena();
$this->mailer->wyslijZamowienie($zamowienie, $cena);
}
return $this->redirect(['index']);
} else {
throw new Exception('Tylko zakończone zamówienia.');
}
Replace Nested Conditional With Guard Clauses - przykład
if ($zamowienie->czyZakonczone() === false) {
throw new Exception('Tylko zakończone zamówienia.');
}
if ($zamowienie->getFirma()->nazwa === 'Januszex 2022 sp. z. o.') {
$tranposrt = $this->spedytor->zarezerwujKubature($zamowienie->getKubatura());
$promocyjnaCena = $this->cennik->standardowaCena() * 5;
$faktura = $this->rozliczenia->wygenerujFakture($zamowienie);
$this->printer->drukuj($faktura);
return $this->redirect(['index']);
}
$cena = $this->cennik->standardowaCena();
$this->mailer->wyslijZamowienie($zamowienie, $cena);
return $this->redirect(['index']);
INTRODUCE
PARAMETER OBJECT
Introduce Parameter Object
Kiedy? Mamy powiązane ze sobą parametry metody, które na dodatek
pojawiają się w różnych metodach. Uwaga!
Co zyskamy? Pozbywamy się zduplikowanego kodu który jest związany
z logiką parametrów, zmniejszamy liczbę parametrów przekazywanych
do metody, zwiększamy czytelność kodu
Jak to zrobić? Tworzymy klasę, która będzie reprezentować nasze
parametry, metody które używały naszych parametrów zmieniamy tak
aby przyjmowały zamiast nich obiekt typu klasy którą stworzyliśmy,
zmieniamy odwołania do parametrów wewnątrz metod na pola,
metody z nowego obiektu
Wady? Technika ta czasami prowadzi do tworzenia klas pozbawionych
funkcjonalności – pojemników na same dane. Co jest nie zgodne z
zasadami OOP.
Introduce Parameter Object - przykład
class PracownikUrlop
{
public function getIloscDniPracujacych(
DateTime $poczatek,
DateTime $koniec): int
{
$iloscDniPracujacych = 0;
if ($poczatek > $koniec) {
throw new Exception('Błędny zakres dat.');
}
$period = new DatePeriod($poczatek,
new DateInterval('P1D'),
$koniec);
foreach ($period as $date) {
if ($date->format('N') < 6) {
$iloscDniPracujacych++;
}
}
}
return $iloscDniPracujacych;
}
public function getUrlop(DateTime $poczatek,
DateTime $koniec): array
{
if ($poczatek > $koniec) {
throw new Exception('Błędny zakres dat.');
}
...
}
}
Introduce Parameter Object - przykład
class ZakresDat
{
public function __construct(DateTime
$poczatek, DateTime $koniec)
{
if ($poczatek > $koniec) {
throw new Exception('Błędny zakres dat.');
}
}
public function getPoczatek(): DateTime
public function getKoniec(): DateTime
public function getPeriod(): DatePeriod
{
return new DatePeriod($this->poczatek,
new DateInterval('P1D'),
$this->koniec);
}
}
class PracownikUrlop
{
public function getIloscDniPracujacych(
ZakresDat $zakresDat): int
{
$iloscDniPracujacych = 0;
foreach ($zakresDat>getPeriod() as $date) {
if ($date->format('N') < 6) {
$iloscDniPracujacych++;
}
}
return $iloscDniPracujacych;
}
public function getUrlop(ZakresDat $zakresDat): array
{
...
}
}
REPLACE ERROR CODE
WITH EXCEPTION
Replace Error Code with Exception
Kiedy? Chcemy zwrócić jakąś wartość do kodu klienckiego oraz w przypadku
błędu zakomunikować to razem z kodem błędu, lecz nie wiemy do końca jak
to zrobić i sięgamy do sposobów z proceduralnego podejścia tylko, że
zapominamy, że obracamy się w obiektowym świecie, poza tym np. nasza
metoda zwraca więcej niż jeden typ.
Co zyskamy? Uciekniemy od proceduralnego paradygmatu i wrócimy w
ramiona OOP, uściślimy zwracany typ danych z metody, wyjątki oprócz kodów
błędów mogę przechowywać dodatkowe informacje np. opis błędu
Jak to zrobić? Zmieniamy wszystkie sprawdzanie zwracanych kodów błędów
na bloki try-catch, wewnątrz metody rzucamy wyjątki zamiast zwracania
kodów błędów
Wady? Używanie wyjątków do przekazywania komunikatów powoduje wystp
wielu bloków try-catch. Pamiętajmy, że wyjątki służą do komunikowania o
błędach!
Uwaga!
Technika to operuje na zwracanym
kodzie błędu – l. całkowita. W
przykładzie posłużę się zamiast tego
tablicą – niestety dość często
stosowanym rozwiązaniem w PHP
Replace Error Code* with Exception - przykład
public function wyliczCene(ZamowieniePozycja $pozycja): array
{
if ($pozycja->getFirma()->nazwa === 'Januszex 2022 sp. z o. o.') {
return [
'status' => 'error',
'msg' => 'Tej firmy nie obsługujemy!'
];
}
if ($pozycja->czyLozko() === false) {
return [
'status' => 'error',
'msg' => 'Wyliczamy cenę tylko dla łóżek'
];
}
return [
'status' => 'success',
'cena_netto' => $this->bardzoZaawansowanaLogika($pozycja),
];
}
Replace Error Code* with Exception - przykład
$cenaNetto = $cennik->wyliczCeneNetto($pozycja);
if ($cenaNetto['status'] === 'error') {
//tutaj obsługujemy błąd
}
$pozycjaFaktury->cenaNetto = $cenaNetto['cena_netto'];
Replace Error Code* with Exception - przykład
class Cennik
{
public function wyliczCeneNetto(ZamowieniePozycja $pozycja): float
{
if ($pozycja->getFirma()->naz === 'Januszex 2022 sp. z o. o.') {
throw new TejFirmyNieObslugujemyException(
'Tej firmy nie obslugujemy!'
);
}
if ($pozycja->czyLozko() === false) {
throw new BlednyTypPozycjiException(
'Wyliczamy cenę tylko dla łóżek'
);
}
return $this->bardzoZaawansowanaLogika($pozycja);
}
}
Replace Error Code* with Exception
try {
$pozycjaFaktury->cenaNetto = $cena->wyliczCeneNetto($pozycja);
} catch (CennikException $e) {
//tutaj obsługujemy błąd
}
EXTRACT SUBCLASS
Extract Subclass
Kiedy? Nasza klasa posiada pola oraz metody używane tylko w jednym
określonym przypadku. Mimo że ten konkretny przypadek wygląda na
odrębny to jest powiązany w jakiś sposób z ogólnym kontekstem przez
co nie może zostać wyodrębniony do w pełni niezależnej klasy.
Co zyskamy? Konkretyzację przypadków z wyodrębnioną logiką
powiązaną właśnie z tym przypadkiem, będziemy mogli łatwo tworzyć
nowe przypadki jako podklasy
Jak to zrobić? Zmieniamy
Wady? Ta technika jest niemożliwa lub trudna w implementacji w
przypadku bardzo rozbudowanych hierarchii klas.
Extract Subclass - przykład
class Akord
{
private $tkaninaID;
private $ramaLozkaID;
private $czyOtwieranaPufa;
public function wyliczCzas(): int
{
$this->przypiszParametryDoWzorow();
$this->wylicz();
$this->optymalizujCzas();
return $this->wyliczonyCzas;
}
private function przypiszParametryDoWzorow(): void
{
//logika przypisywania
if ($this->pozycja->czyLozko()) {
$this->przypiszParametryLozkaDoWzorow();
}
}
private function optymalizujCzas(): void
}
Extract Subclass - przykład
class Akord
{
protected int $tkaninaID;
public function wyliczCzas(): int
{
$this->przypiszParametryDoWzorow();
$this->wylicz();
$this->optymalizujCzas();
return $this->wyliczonyCzas;
}
protected function przypiszParametryDoWzorow(): void
{
//logika przypisywania
}
private function optymalizujCzas(): void
}
class AkordLozko extends Akord
{
private int $ramaLozkaID;
private bool $czyOtwieranaPufa;
protected function przypiszParametryDoWzorow(): void
{
parent::przypiszParametryDoWzorow();
//specjalna logika oparta o typ pozycji = łózko
}
}
CZEMU NIE ZAWSZE
BĘDZIESZ
REFAKTORYZOWAŁ KOD
Czemu nie zawsze będziesz refaktoryzował kod ?
Ponieważ:
• Nie będziesz miał na to czasu – szybki realease, szybka poprawka
błędu itd.
• Nie będziesz potrafił tego zrobić - mam nadzieje, że ta prezentacja
trochę Ci pomoże
• Nikt nie będzie chciał Ci za to zapłacić – a wiadomo za friko to można
pisać ToDo listy ucząc się nowego frameworka w JS’ach
• Nie będziesz umiał uargumentować zasadności refaktoryzacji ze
strony biznesowej dla przełożonego, zleceniodawcy - postaram się coś
podpowiedzieć
• Nie zrobisz tego, bo nie! - podejmiesz świadomą decyzję z którą
będziesz musiał żyć :)
Jak przekonać biznes do refaktoryzacji
• Zrozumieć płaszczyznę, po której porusza się biznes – pieniądze!
• Jeżeli Twoja praca nie przyniesie pieniędzy (zysku) to po co szef / inwestor ma za
to płacić ?
• Pamiętaj, że Twój czas to ich pieniądze !
• Przekonaj ludzi od biznesu, że refaktoryzacja jest inwestycją pełną zysków, tylko,
że w dłuższym terminie.
• Termin ten to nic innego jak przyszłe wdrożenia nowych funkcjonalności lub
utrzymanie istniejącego kodu.
• Owszem na początku ”trzymanie się zasad” wydłuża czas wytwarzania
oprogramowania przez co więcej kosztuje, ale w późniejszym czasie te wartości
się odwracają
• Uwaga! Jeżeli produkt nad którym pracujesz nie ma w planach być rozwijany,
utrzymywany albo jest MVP (minimum viable product) – odpuść refaktoryzację,
oni mają racje, że nie chcą za to zapłacić J
Jak przekonać biznes do refaktoryzacji
Pytania ?
Sugestie ?
Źródła
• https://refactoring.guru
• Refactoring Improving the Design of Existing Code by
Martin Fowler, with Kent Beck 2018
• https://refactoring.com
• https://wikipedia.org

Weitere ähnliche Inhalte

Ähnlich wie Refaktoryzacja

Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHPJak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
Piotr Horzycki
 
Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014
Tomasz Dziuda
 
Od codziennej higieny do strategicznej refaktoryzacji
Od codziennej higieny do strategicznej refaktoryzacjiOd codziennej higieny do strategicznej refaktoryzacji
Od codziennej higieny do strategicznej refaktoryzacji
Michał Bartyzel
 
JDD 2016 - Wojciech Oczkowski - Testowanie Wydajnosci Za Pomoca Narzedzia JMH
JDD 2016 - Wojciech Oczkowski - Testowanie Wydajnosci Za Pomoca Narzedzia JMHJDD 2016 - Wojciech Oczkowski - Testowanie Wydajnosci Za Pomoca Narzedzia JMH
JDD 2016 - Wojciech Oczkowski - Testowanie Wydajnosci Za Pomoca Narzedzia JMH
PROIDEA
 
[JUG, PL] Strategiczna refaktoryzacja
[JUG, PL] Strategiczna refaktoryzacja[JUG, PL] Strategiczna refaktoryzacja
[JUG, PL] Strategiczna refaktoryzacja
Michał Bartyzel
 
Zbyszek Rzepka: GameDev od zaplecza
Zbyszek Rzepka: GameDev od zapleczaZbyszek Rzepka: GameDev od zaplecza
Zbyszek Rzepka: GameDev od zaplecza
GameDesire Academy
 
4Developers 2015: Przejrzysty i testowalny kod na Androidzie? Spróbujmy z Cle...
4Developers 2015: Przejrzysty i testowalny kod na Androidzie? Spróbujmy z Cle...4Developers 2015: Przejrzysty i testowalny kod na Androidzie? Spróbujmy z Cle...
4Developers 2015: Przejrzysty i testowalny kod na Androidzie? Spróbujmy z Cle...
PROIDEA
 
Praktyczne code reviews - PHPConPl
Praktyczne code reviews - PHPConPlPraktyczne code reviews - PHPConPl
Praktyczne code reviews - PHPConPl
Sebastian Marek
 
Angular 4 pragmatycznie
Angular 4 pragmatycznieAngular 4 pragmatycznie
Angular 4 pragmatycznie
Sages
 
Od Produktywności do Sabotażu - Sławomir Radzymiński, KraQA #31
Od Produktywności do Sabotażu - Sławomir Radzymiński, KraQA #31Od Produktywności do Sabotażu - Sławomir Radzymiński, KraQA #31
Od Produktywności do Sabotażu - Sławomir Radzymiński, KraQA #31
kraqa
 
Wprowadzenie do PHPUnit
Wprowadzenie do PHPUnitWprowadzenie do PHPUnit
Wprowadzenie do PHPUnit
Michał Kowalik
 
JDD2014: Strategiczna refaktoryzacja - Michał Bartyzel
JDD2014: Strategiczna refaktoryzacja - Michał BartyzelJDD2014: Strategiczna refaktoryzacja - Michał Bartyzel
JDD2014: Strategiczna refaktoryzacja - Michał Bartyzel
PROIDEA
 
PHP meetup#4 Godek
PHP meetup#4 GodekPHP meetup#4 Godek
PHP meetup#4 Godek
Maciek Godek
 
Mity, które blokują Twoją karierę
Mity, które blokują Twoją karieręMity, które blokują Twoją karierę
Mity, które blokują Twoją karierę
Piotr Horzycki
 
Pułapki programowania obiektowego
Pułapki programowania obiektowego Pułapki programowania obiektowego
Pułapki programowania obiektowego
Adam Sawicki
 
Clean code w Ruby
Clean code w RubyClean code w Ruby
Clean code w Ruby
Rafal Piekarski
 
Wzorce projektowe w praktyce
Wzorce projektowe w praktyceWzorce projektowe w praktyce
Wzorce projektowe w praktyce
PHPstokPHPstok
 
Maciej Ostrowski: Podstawy implementacji multi-inwentarza w Magento
Maciej Ostrowski: Podstawy implementacji multi-inwentarza w MagentoMaciej Ostrowski: Podstawy implementacji multi-inwentarza w Magento
Maciej Ostrowski: Podstawy implementacji multi-inwentarza w MagentoMeet Magento Poland
 
Podstawy testowania oprogramowania INCO 2023.pptx
Podstawy testowania oprogramowania INCO 2023.pptxPodstawy testowania oprogramowania INCO 2023.pptx
Podstawy testowania oprogramowania INCO 2023.pptx
Katarzyna Javaheri-Szpak
 
Strategie automatyzacji testow
Strategie automatyzacji testowStrategie automatyzacji testow
Strategie automatyzacji testow
Wiktor Żołnowski
 

Ähnlich wie Refaktoryzacja (20)

Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHPJak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
 
Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014Daj się wyręczyć - Joomla Day Polska 2014
Daj się wyręczyć - Joomla Day Polska 2014
 
Od codziennej higieny do strategicznej refaktoryzacji
Od codziennej higieny do strategicznej refaktoryzacjiOd codziennej higieny do strategicznej refaktoryzacji
Od codziennej higieny do strategicznej refaktoryzacji
 
JDD 2016 - Wojciech Oczkowski - Testowanie Wydajnosci Za Pomoca Narzedzia JMH
JDD 2016 - Wojciech Oczkowski - Testowanie Wydajnosci Za Pomoca Narzedzia JMHJDD 2016 - Wojciech Oczkowski - Testowanie Wydajnosci Za Pomoca Narzedzia JMH
JDD 2016 - Wojciech Oczkowski - Testowanie Wydajnosci Za Pomoca Narzedzia JMH
 
[JUG, PL] Strategiczna refaktoryzacja
[JUG, PL] Strategiczna refaktoryzacja[JUG, PL] Strategiczna refaktoryzacja
[JUG, PL] Strategiczna refaktoryzacja
 
Zbyszek Rzepka: GameDev od zaplecza
Zbyszek Rzepka: GameDev od zapleczaZbyszek Rzepka: GameDev od zaplecza
Zbyszek Rzepka: GameDev od zaplecza
 
4Developers 2015: Przejrzysty i testowalny kod na Androidzie? Spróbujmy z Cle...
4Developers 2015: Przejrzysty i testowalny kod na Androidzie? Spróbujmy z Cle...4Developers 2015: Przejrzysty i testowalny kod na Androidzie? Spróbujmy z Cle...
4Developers 2015: Przejrzysty i testowalny kod na Androidzie? Spróbujmy z Cle...
 
Praktyczne code reviews - PHPConPl
Praktyczne code reviews - PHPConPlPraktyczne code reviews - PHPConPl
Praktyczne code reviews - PHPConPl
 
Angular 4 pragmatycznie
Angular 4 pragmatycznieAngular 4 pragmatycznie
Angular 4 pragmatycznie
 
Od Produktywności do Sabotażu - Sławomir Radzymiński, KraQA #31
Od Produktywności do Sabotażu - Sławomir Radzymiński, KraQA #31Od Produktywności do Sabotażu - Sławomir Radzymiński, KraQA #31
Od Produktywności do Sabotażu - Sławomir Radzymiński, KraQA #31
 
Wprowadzenie do PHPUnit
Wprowadzenie do PHPUnitWprowadzenie do PHPUnit
Wprowadzenie do PHPUnit
 
JDD2014: Strategiczna refaktoryzacja - Michał Bartyzel
JDD2014: Strategiczna refaktoryzacja - Michał BartyzelJDD2014: Strategiczna refaktoryzacja - Michał Bartyzel
JDD2014: Strategiczna refaktoryzacja - Michał Bartyzel
 
PHP meetup#4 Godek
PHP meetup#4 GodekPHP meetup#4 Godek
PHP meetup#4 Godek
 
Mity, które blokują Twoją karierę
Mity, które blokują Twoją karieręMity, które blokują Twoją karierę
Mity, które blokują Twoją karierę
 
Pułapki programowania obiektowego
Pułapki programowania obiektowego Pułapki programowania obiektowego
Pułapki programowania obiektowego
 
Clean code w Ruby
Clean code w RubyClean code w Ruby
Clean code w Ruby
 
Wzorce projektowe w praktyce
Wzorce projektowe w praktyceWzorce projektowe w praktyce
Wzorce projektowe w praktyce
 
Maciej Ostrowski: Podstawy implementacji multi-inwentarza w Magento
Maciej Ostrowski: Podstawy implementacji multi-inwentarza w MagentoMaciej Ostrowski: Podstawy implementacji multi-inwentarza w Magento
Maciej Ostrowski: Podstawy implementacji multi-inwentarza w Magento
 
Podstawy testowania oprogramowania INCO 2023.pptx
Podstawy testowania oprogramowania INCO 2023.pptxPodstawy testowania oprogramowania INCO 2023.pptx
Podstawy testowania oprogramowania INCO 2023.pptx
 
Strategie automatyzacji testow
Strategie automatyzacji testowStrategie automatyzacji testow
Strategie automatyzacji testow
 

Mehr von PHPstokPHPstok

Jak ograniczyć używanie tablic w PHP.pptx
Jak ograniczyć używanie tablic w PHP.pptxJak ograniczyć używanie tablic w PHP.pptx
Jak ograniczyć używanie tablic w PHP.pptx
PHPstokPHPstok
 
Blaski i cienie pracy Project Managera.pptx
Blaski i cienie pracy Project Managera.pptxBlaski i cienie pracy Project Managera.pptx
Blaski i cienie pracy Project Managera.pptx
PHPstokPHPstok
 
PHP 8.1
PHP 8.1PHP 8.1
Zarządzanie złożonością
Zarządzanie złożonościąZarządzanie złożonością
Zarządzanie złożonością
PHPstokPHPstok
 
Clean Code
Clean CodeClean Code
Clean Code
PHPstokPHPstok
 
Testy mutacyjne
Testy mutacyjneTesty mutacyjne
Testy mutacyjne
PHPstokPHPstok
 
Najczęstsze błędy początkujących programistów PHP
Najczęstsze błędy początkujących programistów PHPNajczęstsze błędy początkujących programistów PHP
Najczęstsze błędy początkujących programistów PHP
PHPstokPHPstok
 
Bezpieczeństwo aplikacji webowych
Bezpieczeństwo aplikacji webowychBezpieczeństwo aplikacji webowych
Bezpieczeństwo aplikacji webowych
PHPstokPHPstok
 
Sztuka samodoskonalenia programisty
Sztuka samodoskonalenia programistySztuka samodoskonalenia programisty
Sztuka samodoskonalenia programisty
PHPstokPHPstok
 
Testy jednostkowe - PHPUnit
Testy jednostkowe - PHPUnitTesty jednostkowe - PHPUnit
Testy jednostkowe - PHPUnit
PHPstokPHPstok
 
Docker
DockerDocker
SOLID
SOLIDSOLID
PSR czyli dobre praktyki programistyczne
PSR czyli dobre praktyki programistycznePSR czyli dobre praktyki programistyczne
PSR czyli dobre praktyki programistyczne
PHPstokPHPstok
 

Mehr von PHPstokPHPstok (13)

Jak ograniczyć używanie tablic w PHP.pptx
Jak ograniczyć używanie tablic w PHP.pptxJak ograniczyć używanie tablic w PHP.pptx
Jak ograniczyć używanie tablic w PHP.pptx
 
Blaski i cienie pracy Project Managera.pptx
Blaski i cienie pracy Project Managera.pptxBlaski i cienie pracy Project Managera.pptx
Blaski i cienie pracy Project Managera.pptx
 
PHP 8.1
PHP 8.1PHP 8.1
PHP 8.1
 
Zarządzanie złożonością
Zarządzanie złożonościąZarządzanie złożonością
Zarządzanie złożonością
 
Clean Code
Clean CodeClean Code
Clean Code
 
Testy mutacyjne
Testy mutacyjneTesty mutacyjne
Testy mutacyjne
 
Najczęstsze błędy początkujących programistów PHP
Najczęstsze błędy początkujących programistów PHPNajczęstsze błędy początkujących programistów PHP
Najczęstsze błędy początkujących programistów PHP
 
Bezpieczeństwo aplikacji webowych
Bezpieczeństwo aplikacji webowychBezpieczeństwo aplikacji webowych
Bezpieczeństwo aplikacji webowych
 
Sztuka samodoskonalenia programisty
Sztuka samodoskonalenia programistySztuka samodoskonalenia programisty
Sztuka samodoskonalenia programisty
 
Testy jednostkowe - PHPUnit
Testy jednostkowe - PHPUnitTesty jednostkowe - PHPUnit
Testy jednostkowe - PHPUnit
 
Docker
DockerDocker
Docker
 
SOLID
SOLIDSOLID
SOLID
 
PSR czyli dobre praktyki programistyczne
PSR czyli dobre praktyki programistycznePSR czyli dobre praktyki programistyczne
PSR czyli dobre praktyki programistyczne
 

Refaktoryzacja

  • 2. O mnie • Marek Keżel • 7 lat doświadczenia w PHP (5.6, 7, 8, 9, 10, 11, 12 J) • Programista w Innovation Software Sp. z o. o. • Niezależny konsultant ds. oprogramowania w Specific Sp. z o. o. • NDProdukcja – twórca wewnętrznego systemu ERP jednego z największych producentów mebli tapicerowanych w Polsce • Mąż i Tata
  • 3. Agenda •Czym jest refaktoryzacja •Szybki przegląd technik refaktoryzacji •Omówienie 8 wybranych technik •Czemu nie zawsze będziesz refaktoryzował kod •Pytania, sugestie, ewentualne wygwizdanie prelegenta
  • 5. Czym jest refaktoryzacja ? Refaktoryzacja to wymyślony proces przez rozpieszczonych programistów, którzy myślą, że głupi ludzie zapłacą im ponownie za tą samą pracę albo co gorsze za udawanie, że coś robią. Janusz, dyrektor operacyjny, członek zarządu
  • 6. Czym jest refaktoryzacja ? The main purpose of refactoring is to fight technical debt. It transforms a mess into clean code and simple design. https://refactoring.guru/refactoring/what-is-refactoring In computer programming and software design, code refactoring is the process of restructuring existing computer code—changing the factoring—without changing its external behavior. Refactoring is intended to improve the design, structure, and/or implementation of the software (its non-functional attributes), while preserving its functionality. https://en.wikipedia.org/wiki/Code_refactoring
  • 7. Czym jest refaktoryzacja ? Głównym celem przeprowadzania refaktoryzacji jest walka z długiem technologicznym*, przeistoczenie brzydkiego kodu w czysty* ZROZUMIAŁY DLA INNYCH. *Dług technologiczny (Technical Debt) jest naszym mniej lub bardziej świadomym działaniem, które ciągnie nasze oprogramowanie w dół. Ignorujemy dobre praktyki w imię wyższych celów (tak nam się wydaje…) jak np. szybszy release przez co pomijamy pisanie testów, nie kontrolujemy zależności, tworzymy niezrozumiałe nazwy pól oraz metod. Czasami jesteśmy „dłużni” ponieważ robimy to nieświadomie – nie mamy wiedzy jak coś zrobić lepiej. Czym się objawia dług? Wydłużonym czasem wprowadzania zmian w kodzie, większą ilość błędów, coraz większą ilością próśb o przeniesienie do innego projektu. *Czysty kod (Clean Code) to taki który jest łatwy w utrzymaniu, zrozumiały dla innych programistów (bez żadnej magii, dziwacznego nazewnictwa, rozdętych metod oraz klas), bez zduplikowanego kodu, pozbawiony tzw. zapaszków (Code Smells). Czysty kod czyta się jak dobrą książkę z uśmiechem a nie z przerażeniem w stylu „co to $%^& jest !?”.
  • 9. Kiedy wykonywać refaktoryzację ? • Kiedy patrzymy na kod i czujemy, że skręca nas w środku na sam widok – to jest idealny moment! • Kiedy dodajemy nową funkcjonalność do naszego programu (obie czynności traktujemy oddzielnie!) • Kiedy naprawiamy jakiś problem • Kiedy robimy codereview • Kiedy robimy coś i mamy Déjà vu, że coś takiego już robiliśmy
  • 10. Kiedy NIE wykonywać refaktoryzacji ? • Kiedy nie rozumiemy kodu nad którym pracujemy • Kiedy kod już działa produkcyjnie i nie ma z nim żadnych problemów • Kiedy projekt jest w stylu napisz i zapomnij • Kiedy nie mamy pokrycia w testach tego co chcemy zrefaktoryzować* *dopiszemy te testy lub każdy z nas podejmie super ważną decyzje mając z tyłu głowy złotą zasadę (przetestujemy na produkcji) J
  • 11. Jak wykonać refaktoryzację ? „Refaktoryzacja powinna być wykonana jako seria małych zmian które czynią nasz kod lepszym, pozostawiając jednocześnie program w stanie gotowości” • Jeżeli kod po wykonaniu refaktoryzacji dalej jest „brzydki” wracamy do początku i robimy to jeszcze raz. • Podczas refaktoryzacji nie powinny powstawać żadne nowe funkcjonalności (proces refaktoryzacji powinien być oddzielny) • Wszystkie istniejące testy muszą zostać poprawnie wykonane (czasami to testy są winne niepowodzeń np. są zbyt nisko poziomowe, przez co musimy dokonać refaktoryzacji samych testów) https://refactoring.guru/refactoring/how-to
  • 12. SZYBKI PRZEGLĄD TECHNIK REFAKTORYZACJI Na podstawie: https://refactoring.guru/refactoring/techniques
  • 13. Kompozycja metod • Extract Method • Inline Method • Extract Variable • Inline Temp • Replace Temp with Query • Split Temporary Variable • Remove Assignments to Parameters • Replace Method with Method Object • Substitute Algorithm
  • 14. Przenoszenie funkcjonalności • Move Method • Move Field • Extract Class • Inline Class • Hide Delegate • Remove Middle Man • Introduce Foreign Method • Introduce Local Extension
  • 15. Ogranizacja danych • Change Value to Reference • Change Reference to Value • Duplicate Observed Data • Self Encapsulate Field • Replace Data Value with Object • Replace Array with Object • Change Unidirectional Association to Bidirectional • Change Bidirectional Association to Unidirectional • Encapsulate Field • Encapsulate Collection • Replace Magic Number with Symbolic Constant • Replace Type Code with Class • Replace Type Code with Subclasses • Replace Type Code with State/Strategy • Replace Subclass with Fields
  • 16. Upraszczanie wyrażeń regulanych • Consolidate Conditional Expression • Consolidate Duplicate Conditional Fragments • Decompose Conditional • Replace Conditional with Polymorphism • Remove Control Flag • Replace Nested Conditional with Guard Clauses • Introduce Null Object • Introduce Assertion
  • 17. Upraszczanie wywołań metod / funkcji • Add Parameter • Remove Parameter • Rename Method • Separate Query from Modifier • Parameterize Method • Introduce Parameter Object • Preserve Whole Object • Remove Setting Method • Replace Parameter with Explicit Methods • Replace Parameter with Method Call • Hide Method • Replace Constructor with Factory Method • Replace Error Code with Exception • Replace Exception with Test
  • 18. Upraszczanie abstrakcji • Pull Up Field • Pull Up Method • Pull Up Constructor Body • Push Down Field • Push Down Method • Extract Subclass • Extract Superclass • Extract Interface • Collapse Hierarchy • Form Template Method • Replace Inheritance with Delegation • Replace Delegation with Inheritance
  • 20. UWAGA! W celu uproszczenia a zarazem zmieszczenia się w limicie czasowym, nie wszystkie drobne kroki refaktoryzacji zostaną pokazane na slajdach.* *Ale prowadzący o nich wspomni :)
  • 22. Extract Method Kiedy? Metoda ma za dużo odpowiedzialności (narusza SRP). Czujemy, że coś nam nie pasuje. Co zyskamy? Ograniczymy odpowiedzialność metody, zwiększymy czytelność kodu. Jak to zrobić? Tworzymy nową metodę (modyfikator dostępu?), przenosimy fragment kodu do nowej metody, jeżeli kod korzysta ze zmiennych zadeklarowanych wcześniej – przekazujemy je, w bazowej metodzie odwołujemy się do nowo utworzonej, wykonujemy testy Metoda odwrotna? Inline Method
  • 23. Extract Method - przykład class OptymalizerkaBoki { public function wyslijFormatkiNaOptymalizerke(array $formatki, int $optymalizerkaID): void { $formatkiWezglowia = []; foreach ($formatki as $formatka) { if ($formatka->czyWezglowie() === false) { continue; } $formatkiWezglowia[] = $formatka; } $optymalizerkaWiadomosc = new OptymalizerkaWiadomosc(); $optymalizerkaWiadomosc->setFormatki($formatkiWezglowia); $this->getBus($optymalizerkaID)->wyslij($optymalizerkaWiadomosc); } }
  • 24. Extract Method - przykład class OptymalizerkaBokiRefactoryzacja { public function wyslijFormatkiNaOptymalizerke(array $formatki, int $optymalizerkaID): void { $formatkiWezglowia = $this->wyfiltrujFormatkiWezglowia($formatki); $optymalizerkaWiadomosc = new OptymalizerkaWiadomosc(); $optymalizerkaWiadomosc->setFormatki($formatkiWezglowia); $this->getBus($optymalizerkaID)->wyslij($optymalizerkaWiadomosc); } private function wyfiltrujFormatkiWezglowia(array $formatki): array { $formatkiWezglowia = []; foreach ($formatki as $formatka) { if ($formatka->czyWezglowie() === false) { continue; } $formatkiWezglowia[] = $formatka; } return $formatkiWezglowia; } }
  • 26. Exctract Class Kiedy? Klasa posiada wiele odpowiedzialności, robi za dużo, narusza zasadę SRP (Single Responsibility Principle), jest mniej czytelna i trudna w analizie Co zyskamy? Zbliżymy się albo osiągniemy zgodność z zasada SRP, zwiększymy tolerancję na zmiany, poprawimy czytelność oraz zrozumienie kodu Jak to zrobić? Tworzymy nową klasę oraz tworzymy relację między klasami (najlepiej jednokierunkową) przenosimy do niej docelową funkcjonalność (korzystamy z technik Move Field i Move Method) uważamy na modyfikatory dostępu (zaczynamy od prywatnych), wykonujemy testy Wady? Zbyt często wykorzystywana, powoduje mnóstwo nowych klas często małych i nie koniecznie z odpowiednimi odpowiedzialnościami Metoda odwrotna? Inline Class
  • 27. Extract Class - przykład class SaturnService { public function __construct() { $this->client = new SoapClient(self::SATURN_API_WSDL, $this->options); } public function potwierdzZamowienieDoDostawcy(Zamowienie $zamowienie): void { // coś robimy z zamówieniem oraz pobieramy docID $response = $this->potwierdzZamowienieWyslijRequest($saturnDocID); //coś robimy z odpowiedzą dalsza cześc logiki } private function potwierdzZamowienieWyslijRequest(string $saturnDocID): array { $result = $this->client->ZdConfirm(['ZdConfirm' => ['DocID' => $saturnDocID]]); // cos robimy z rezultatem return ['stauts' => $result->status, 'confirmCode' => $result->confirmCode]; } }
  • 28. Extract Class - przykład class SaturnApi implements SaturnApiInterface { public function __construct() { $this->soapClient = new SoapClient(self::SATURN_API_WSDL, $this->options); } public function potwierdzZamowienieWyslijRequest(string $saturnDocID): array { $result = $this->soapClient->ZdConfirm(['ZdConfirm' => ['DocID' => $saturnDocID]]); // cos robimy z rezultatem return ['stauts' => $result->status, 'confirmCode' => $result->confirmCode]; } }
  • 29. Extract Class - przykład class SaturnService { public function __construct(SaturnApiInterface $api) { $this->apiClient = $api; } public function potwierdzZamowienieDoDostawcy(Zamowienie $zamowienie): void { // cos robimy z zamówieniem oraz pobieramy docID $saturnDocID = ''; $response = $this->apiClient->potwierdzZamowienieWyslijRequest($saturnDocID); //coś robimy z odpowiedzą dalsza cześc logiki } }
  • 31. Replace Array With Object Kiedy? Jak z danymi w tablicy musimy zrobić coś więcej niż tylko je wyświetlić. Co zyskamy? Ułatwimy pracę ze strukturą danych (dopilnujemy właściwych danych we właściwych miejscach). Uzyskamy enkapsulację – połączymy dane z funkcjonalnością – OOP. Jak to zrobić? Tworzymy strukturę nowej klasy w oparciu o źródłową tablicę, zmieniamy w kodzie każde użycie tablicy na nasz nowy obiekt (jeżeli nie możemy, to musimy zaimplementować funkcjonalność tablicy w naszej nowej klasie) Wady? Obiekty są „wolniejsze” zajmują więcej pamięci*. *https://stackoverflow.com/questions/3709578/using-arrays-vs-objects-for-storing-data
  • 32. Replace Array With Object - przykład $wyliczonaFromatka = [ 'id' => '1', 'typ' => 'WZG', 'nazwa' => 'Wezglowie_Panel_X', 'szerokosc' => 1000, 'wysokosc' => 1000, 'grubosc' => 3, 'ilosc' => 12, 'surowiecID' => 123, 'surowiecNazwa' => 'Plyta HDF', ];
  • 33. Replace Array With Object - przykład class CentrumService { public function wyslijFormatkeDoCentrum(array $formatka): void { //czy struktura tablicy $formatka jest poprawna ? $kubatura = $this->wyliczKubature($formatka); $polePowierzchni = $this->wyliczPolePowierzchni($formatka); ... } public function wyliczKubature(array $formatki): float public function wyliczPolePowierzchni(array $formatki): float }
  • 34. Replace Array With Object - przykład class WyliczonaFormatka { private int $id; private string $typ; private string $nazwa; … public static function utworzZTablicy(array $formatka): self { //sprawdzenie poprawnosci otrzymanej tablicy return new self($formatka['id'], $formatka['typ'], $formatka['nazwa']); } public function __construct(int $id, string $typ, string $nazwa) public function getKubatura(): float public function getPolePowierzchni(): float }
  • 35. Replace Array With Object - przykład class CentrumService { public function wyslijFormatkeDoCentrum(WyliczonaFormatka $wyliczonaFormatka): void { $kubatura = $wyliczonaFormatka->getKubatura(); $polePowierzchni = $wyliczonaFormatka->getPolePowierzchni(); ... } }
  • 37. Consolidate Duplicate Conditional Fragments Kiedy? Posiadamy zduplikowany kod w różnych gałęziach warunkowych Co zyskamy? Usuniemy zduplikowany kod J Jak to zrobić? Przenosimy zduplikowany kod przed lub za (zależnie od jego lokalizacji) instrukcje warunkowe
  • 38. Consolidate Duplicate Conditional Fragments - przykład public function actionCreate() { $zamowienie = new Zamowienie(); $zamowienie->load($this->request->post('zamowienie')); if ($this->request->post('lozko')) { $zamowienie->load($this->request->post('lozko')); $zamowienie->save(); return $this->redirect(['index']); } elseif ($this->request->post('szafka')) { $zamowienie->load($this->request->post('szafka')); $zamowienie->save(); return $this->redirect(['index']); } else { $zamowienie->save(); return $this->redirect(['index']); } }
  • 39. Consolidate Duplicate Conditional Fragments - przykład public function actionCreate() { $zamowienie = new Zamowienie(); $zamowienie->load($this->request->post('zamowienie')); if ($this->request->post('lozko')) { $zamowienie->load($this->request->post('lozko')); } elseif ($this->request->post('szafka')) { $zamowienie->load($this->request->post('szafka')); } $zamowienie->save(); return $this->redirect(['index']); }
  • 41. Replace Nested Conditional With Guard Clauses Kiedy? Mamy mocno zagnieżdżone instrukcje warunkowe, które tworzą kształt grotu strzały. Ciężko jest określić który warunek gdzie się kończy i co robi Co zyskamy? „Wyprostujemy” nasz kod, zlikwidujemy głębokie zagnieżdżenia przez co zwiększymy czytelność kodu Jak to zrobić? Wprowadzamy w każdej instrukcji warunkowej tzw. Klauzule Ochronne (Guard Clauses), które automatycznie zwracają jakąś wartość lub przerywają działanie programu wyjątkiem
  • 42. Replace Nested Conditional With Guard Clauses - przykład if ($zamowienie->czyZakonczone()) { if ($zamowienie->getFirma()->nazwa === 'Januszex 2022 Sp. z. o.') { $tranposrt = $this->spedytor->zarezerwujKubature($zamowienie->getKubatura()); $promocyjnaCena = $this->cennik->standardowaCena() * 5; $faktura = $this->rozliczenia->wygenerujFakture($zamowienie); $this->printer->drukuj($faktura); } else { $cena = $this->cennik->standardowaCena(); $this->mailer->wyslijZamowienie($zamowienie, $cena); } return $this->redirect(['index']); } else { throw new Exception('Tylko zakończone zamówienia.'); }
  • 43. Replace Nested Conditional With Guard Clauses - przykład if ($zamowienie->czyZakonczone() === false) { throw new Exception('Tylko zakończone zamówienia.'); } if ($zamowienie->getFirma()->nazwa === 'Januszex 2022 sp. z. o.') { $tranposrt = $this->spedytor->zarezerwujKubature($zamowienie->getKubatura()); $promocyjnaCena = $this->cennik->standardowaCena() * 5; $faktura = $this->rozliczenia->wygenerujFakture($zamowienie); $this->printer->drukuj($faktura); return $this->redirect(['index']); } $cena = $this->cennik->standardowaCena(); $this->mailer->wyslijZamowienie($zamowienie, $cena); return $this->redirect(['index']);
  • 45. Introduce Parameter Object Kiedy? Mamy powiązane ze sobą parametry metody, które na dodatek pojawiają się w różnych metodach. Uwaga! Co zyskamy? Pozbywamy się zduplikowanego kodu który jest związany z logiką parametrów, zmniejszamy liczbę parametrów przekazywanych do metody, zwiększamy czytelność kodu Jak to zrobić? Tworzymy klasę, która będzie reprezentować nasze parametry, metody które używały naszych parametrów zmieniamy tak aby przyjmowały zamiast nich obiekt typu klasy którą stworzyliśmy, zmieniamy odwołania do parametrów wewnątrz metod na pola, metody z nowego obiektu Wady? Technika ta czasami prowadzi do tworzenia klas pozbawionych funkcjonalności – pojemników na same dane. Co jest nie zgodne z zasadami OOP.
  • 46. Introduce Parameter Object - przykład class PracownikUrlop { public function getIloscDniPracujacych( DateTime $poczatek, DateTime $koniec): int { $iloscDniPracujacych = 0; if ($poczatek > $koniec) { throw new Exception('Błędny zakres dat.'); } $period = new DatePeriod($poczatek, new DateInterval('P1D'), $koniec); foreach ($period as $date) { if ($date->format('N') < 6) { $iloscDniPracujacych++; } } } return $iloscDniPracujacych; } public function getUrlop(DateTime $poczatek, DateTime $koniec): array { if ($poczatek > $koniec) { throw new Exception('Błędny zakres dat.'); } ... } }
  • 47. Introduce Parameter Object - przykład class ZakresDat { public function __construct(DateTime $poczatek, DateTime $koniec) { if ($poczatek > $koniec) { throw new Exception('Błędny zakres dat.'); } } public function getPoczatek(): DateTime public function getKoniec(): DateTime public function getPeriod(): DatePeriod { return new DatePeriod($this->poczatek, new DateInterval('P1D'), $this->koniec); } } class PracownikUrlop { public function getIloscDniPracujacych( ZakresDat $zakresDat): int { $iloscDniPracujacych = 0; foreach ($zakresDat>getPeriod() as $date) { if ($date->format('N') < 6) { $iloscDniPracujacych++; } } return $iloscDniPracujacych; } public function getUrlop(ZakresDat $zakresDat): array { ... } }
  • 49. Replace Error Code with Exception Kiedy? Chcemy zwrócić jakąś wartość do kodu klienckiego oraz w przypadku błędu zakomunikować to razem z kodem błędu, lecz nie wiemy do końca jak to zrobić i sięgamy do sposobów z proceduralnego podejścia tylko, że zapominamy, że obracamy się w obiektowym świecie, poza tym np. nasza metoda zwraca więcej niż jeden typ. Co zyskamy? Uciekniemy od proceduralnego paradygmatu i wrócimy w ramiona OOP, uściślimy zwracany typ danych z metody, wyjątki oprócz kodów błędów mogę przechowywać dodatkowe informacje np. opis błędu Jak to zrobić? Zmieniamy wszystkie sprawdzanie zwracanych kodów błędów na bloki try-catch, wewnątrz metody rzucamy wyjątki zamiast zwracania kodów błędów Wady? Używanie wyjątków do przekazywania komunikatów powoduje wystp wielu bloków try-catch. Pamiętajmy, że wyjątki służą do komunikowania o błędach!
  • 50. Uwaga! Technika to operuje na zwracanym kodzie błędu – l. całkowita. W przykładzie posłużę się zamiast tego tablicą – niestety dość często stosowanym rozwiązaniem w PHP
  • 51. Replace Error Code* with Exception - przykład public function wyliczCene(ZamowieniePozycja $pozycja): array { if ($pozycja->getFirma()->nazwa === 'Januszex 2022 sp. z o. o.') { return [ 'status' => 'error', 'msg' => 'Tej firmy nie obsługujemy!' ]; } if ($pozycja->czyLozko() === false) { return [ 'status' => 'error', 'msg' => 'Wyliczamy cenę tylko dla łóżek' ]; } return [ 'status' => 'success', 'cena_netto' => $this->bardzoZaawansowanaLogika($pozycja), ]; }
  • 52. Replace Error Code* with Exception - przykład $cenaNetto = $cennik->wyliczCeneNetto($pozycja); if ($cenaNetto['status'] === 'error') { //tutaj obsługujemy błąd } $pozycjaFaktury->cenaNetto = $cenaNetto['cena_netto'];
  • 53. Replace Error Code* with Exception - przykład class Cennik { public function wyliczCeneNetto(ZamowieniePozycja $pozycja): float { if ($pozycja->getFirma()->naz === 'Januszex 2022 sp. z o. o.') { throw new TejFirmyNieObslugujemyException( 'Tej firmy nie obslugujemy!' ); } if ($pozycja->czyLozko() === false) { throw new BlednyTypPozycjiException( 'Wyliczamy cenę tylko dla łóżek' ); } return $this->bardzoZaawansowanaLogika($pozycja); } }
  • 54. Replace Error Code* with Exception try { $pozycjaFaktury->cenaNetto = $cena->wyliczCeneNetto($pozycja); } catch (CennikException $e) { //tutaj obsługujemy błąd }
  • 56. Extract Subclass Kiedy? Nasza klasa posiada pola oraz metody używane tylko w jednym określonym przypadku. Mimo że ten konkretny przypadek wygląda na odrębny to jest powiązany w jakiś sposób z ogólnym kontekstem przez co nie może zostać wyodrębniony do w pełni niezależnej klasy. Co zyskamy? Konkretyzację przypadków z wyodrębnioną logiką powiązaną właśnie z tym przypadkiem, będziemy mogli łatwo tworzyć nowe przypadki jako podklasy Jak to zrobić? Zmieniamy Wady? Ta technika jest niemożliwa lub trudna w implementacji w przypadku bardzo rozbudowanych hierarchii klas.
  • 57. Extract Subclass - przykład class Akord { private $tkaninaID; private $ramaLozkaID; private $czyOtwieranaPufa; public function wyliczCzas(): int { $this->przypiszParametryDoWzorow(); $this->wylicz(); $this->optymalizujCzas(); return $this->wyliczonyCzas; } private function przypiszParametryDoWzorow(): void { //logika przypisywania if ($this->pozycja->czyLozko()) { $this->przypiszParametryLozkaDoWzorow(); } } private function optymalizujCzas(): void }
  • 58. Extract Subclass - przykład class Akord { protected int $tkaninaID; public function wyliczCzas(): int { $this->przypiszParametryDoWzorow(); $this->wylicz(); $this->optymalizujCzas(); return $this->wyliczonyCzas; } protected function przypiszParametryDoWzorow(): void { //logika przypisywania } private function optymalizujCzas(): void } class AkordLozko extends Akord { private int $ramaLozkaID; private bool $czyOtwieranaPufa; protected function przypiszParametryDoWzorow(): void { parent::przypiszParametryDoWzorow(); //specjalna logika oparta o typ pozycji = łózko } }
  • 60. Czemu nie zawsze będziesz refaktoryzował kod ? Ponieważ: • Nie będziesz miał na to czasu – szybki realease, szybka poprawka błędu itd. • Nie będziesz potrafił tego zrobić - mam nadzieje, że ta prezentacja trochę Ci pomoże • Nikt nie będzie chciał Ci za to zapłacić – a wiadomo za friko to można pisać ToDo listy ucząc się nowego frameworka w JS’ach • Nie będziesz umiał uargumentować zasadności refaktoryzacji ze strony biznesowej dla przełożonego, zleceniodawcy - postaram się coś podpowiedzieć • Nie zrobisz tego, bo nie! - podejmiesz świadomą decyzję z którą będziesz musiał żyć :)
  • 61. Jak przekonać biznes do refaktoryzacji • Zrozumieć płaszczyznę, po której porusza się biznes – pieniądze! • Jeżeli Twoja praca nie przyniesie pieniędzy (zysku) to po co szef / inwestor ma za to płacić ? • Pamiętaj, że Twój czas to ich pieniądze ! • Przekonaj ludzi od biznesu, że refaktoryzacja jest inwestycją pełną zysków, tylko, że w dłuższym terminie. • Termin ten to nic innego jak przyszłe wdrożenia nowych funkcjonalności lub utrzymanie istniejącego kodu. • Owszem na początku ”trzymanie się zasad” wydłuża czas wytwarzania oprogramowania przez co więcej kosztuje, ale w późniejszym czasie te wartości się odwracają • Uwaga! Jeżeli produkt nad którym pracujesz nie ma w planach być rozwijany, utrzymywany albo jest MVP (minimum viable product) – odpuść refaktoryzację, oni mają racje, że nie chcą za to zapłacić J
  • 62. Jak przekonać biznes do refaktoryzacji
  • 64. Źródła • https://refactoring.guru • Refactoring Improving the Design of Existing Code by Martin Fowler, with Kent Beck 2018 • https://refactoring.com • https://wikipedia.org