.

Jakie są narzędzia i techniki refaktoringu, których warto używać?

Refaktoryzacja to proces zmian, który finalnie pozwala na osiągnięcie czytelnego, łatwego w rozwoju i utrzymaniu kodu. Podczas tych usprawnień jego zachowanie nie powinno się zmieniać. Stąd też należy podchodzić do tematu z dużą dozą ostrożności i rozwagi.

Techniki refaktoringu

Istnieje wiele specyficznych sposobów na refaktoryzacje poszczególnych elementów aplikacji. Dobór odpowiednich technik do rozważanego problemu jest kluczowy do optymalnego przeprowadzenia procesu. Wśród podstawowych zasad, których należy się trzymać jest między innymi to, że kod po refaktoryzacji musi być czystszy, a wszystkie testy muszą przechodzić. Natomiast podczas procesu absolutnie nie należy wprowadzać nowych funkcjonalności.

Technik refaktoringu jest dużo, a nawet bardzo dużo. W tym artykule wymienię kilka z nich. Nie każda metoda ma zastosowanie we wszystkich językach programowania. Techniki refaktoryzacji dzielimy na grupy, w zależności od problemu, który rozwiązują.

Wyodrębnienie, tworzenie i uproszczenie metod i wywołań

Jeśli Twój kod zawiera długie, skomplikowane i trudne w utrzymaniu metody oraz wiele odpowiedzialności, ta grupa technik refaktoryzacji pomoże Ci go oczyścić.

Wyodrębnienie metody

Jeśli metoda jest długa i trudna w zrozumieniu, to określoną, możliwą do zgrupowania część należy wyodrębnić i przenieść do osobnej metody.

Przed refaktoryzacją:

public function printVariables() {
  $this->printFoo();

  print "bar: " . $this->bar;
  print "foobar: " . $this->foobar;
}

Po refaktoryzacji:

public function printAll() {
  $this->printFooRelated();
  $this->printBarRelated();
}

public function printBarRelated() {
  print “bar: “ . $this->bar;
  print “foobar: “ . $this->foobar;
}

Wyodrębnienie zmiennej

Podczas procesu refaktoringu możemy napotkać skomplikowane wyrażenia, które na pierwszy rzut oka mogą być trudne w zrozumieniu. W tym przypadku staramy się wyodrębnić poszczególne części wyrażenia i przenieść je do zmiennych.

Przed refaktoryzacją:

if ($item->isPriceSet() && $item->getPrice() <= 100 && $user->isUserLoggedIn() && $this->isUserAllowedToBuy()) {
  $this->redirectToBuyForm();
}

Po refaktoryzacji:

$isUserAllowed = $user->isUserLoggedIn() && $this->isUserAllowedToBuy();
$isPriceAcceptable = $item->isPriceSet() && $item->getPrice() <= 100;

if ($isUserAllowed && $isPriceAcceptable) {
  $this->redirectToBuyForm();
}

Metoda liniowa

Jeśli jakaś metoda jest wykorzystywana jedynie raz, jest mała i jej zawartość mówi więcej niż sama metoda, możemy rozważyć przeniesienie zawartości metody w miejsce, w którym jest wywoływana.

Przed refaktoryzacją:

public function getFoo() {
  return $this->bar() ? self::FOO : self::FOOBAR;
}

public function bar() {
  return $this->foo === ‘bar’;
}

Po refaktoryzacji:

public function getFoo() {
  return $this->foo === ‘bar’ ? self::FOO : self::FOOBAR;
}

Przenoszenie funkcjonalności między obiektami

Jeśli w refaktoryzowanej funkcjonalności istnieje metoda, która jest częściej wykorzystywana w zakresie innej funkcjonalności, należy daną metodę przenieść do klasy, która używa jej najbardziej. Jeśli jest to metoda wykorzystywana w wielu miejscach - a klasa, która ją implementuje zawiera również inne odpowiedzialności - należy wyodrębnić metodę do odrębnej klasy. Jeśli zadaniem pewnej metody jest jedynie wywołanie innej metody, określamy ją mianem Middle Man, jej wartość jest zerowa i powinniśmy ją usunąć.

Organizacja danych

W tej kategorii mieszczą się wszystkie metody refaktoringu, skupiające się na organizacji kodu. Wykorzystanie zawartych w tej sekcji metod sprawia, że kod jest bardziej czytelny i lepiej zorganizowany.

Enkapsulacja pola

Wszystkie publiczne pola klasy powinny zostać zamienione na prywatne, jeśli nie istnieje dobry powód, aby tego nie robić. Dostęp do zapisu i odczytu jej wartości powinien odbywać się za pomocą getterów i setterów.

Przed refaktoryzacją:

class Foo {
  public $bar;
}

Po refaktoryzacji:

class Foo {
  private $bar;

  public function getFoo() {
    return $this->bar;
  }

  public function setFoo($value) {
    $this->bar = $value;
  }
}

Zamiana magicznych liczb na stałe

Dla programistów piszących funkcjonalność pewna jej część może być oczywista, ale przestanie taka być po kilku miesiącach. Trzeba o tym pamiętać pisząc kod. Wiele osób o tym zapomina i pozostawia w kodzie ciągi znaków i liczby w formie prymitywnej, co nowym osobom uniemożliwia pełne zrozumienie kodu. Podczas procesu refaktoringu wszystkie “magiczne liczby” powinny zostać przeniesione do stałych, o zwięzłych, lecz opisowych nazwach.

Przed refaktoryzacją:

class Foo {
  public function isUnderLimit() {
    return $this->bar <= 20; 
  }
}

Po refaktoryzacji:

class Foo {
  const BAR_LIMIT = 20;

  public function isUnderLimit() {
    return $this->bar <= self::BAR_LIMIT; 
  }
}

Narzędnia do refaktoringu

Istnieje sporo narzędzi pomocnych podczas procesu refaktoringu. Zapoznaj się z funkcjonalnością dostępną w Twoim edytorze kodu. W wielu nowych IDE narzędzia tego typu są wbudowane lub dostępne jako darmowe wtyczki. Dodatkowym pomocnym elementem, który pozwala na pisanie lepszego kodu są:

  • code sniffery - sprawdzają zgodność kodu ze standardami,
  • statyczne analizatory kodu - analizują statycznie zachowanie kodu i pozwalają wyłuskać błędy jeszcze przed uruchomieniem kodu.

W języku programowania PHP najczęściej spotykane są php_codesniffer oraz phpstan.

Techniki refaktoringu - podsumowanie

Proces refaktoryzowania kodu musi zostać poprawnie zaplanowany i poprzedzony szeregiem działań, mających na celu jego zoptymalizowanie. Wymienione narzędzia i techniki w połączeniu ze zrozumieniem, które elementy aplikacji są problematyczne, dadzą Ci pewność, że refaktoryzacja zostanie przeprowadzona holistycznie i poprawnie. Przedstawione przez nas narzędzia i techniki są jedynie zarysem rozwiązań, wypracowanych przez całą społeczność programistów. Jeśli potrzebujesz pomocy z wyborem tego typu rozwiązań i przeprowadzeniem procesu refaktoringu, możemy się tym zająć w ramach PHP developmentu.

W ramach wsparcia dla Drupala utrzymujemy istniejące strony internetowe i rozbudowujemy je o nowe funkcjonalności