Jaki jest cel refaktoryzacji w konkretnym przypadku?
Załóżmy, że w celu pogodzenia się z moją odpowiedzią wszyscy wierzymy (do pewnego stopnia) w TDD (rozwój oparty na testach).
Jeśli celem refaktoryzacji jest wyczyszczenie istniejącego kodu bez zmiany istniejącego zachowania, wówczas pisanie testów przed refaktoryzacją jest sposobem upewnienia się, że nie zmieniłeś zachowania kodu, jeśli ci się powiedzie, testy powiodą się zarówno przed, jak i po refaktoryzujesz.
Testy pomogą ci upewnić się, że nowa praca rzeczywiście działa.
Testy prawdopodobnie odkryją również przypadki, w których oryginalne dzieło nie działa.
Ale w jaki sposób naprawdę dokonujesz znaczącego refaktoryzacji bez wpływu na zachowanie niektórych osób stopniu ?
Oto krótka lista kilku rzeczy, które mogą się zdarzyć podczas refaktoryzacji:
- zmień nazwę zmiennej
- funkcja zmiany nazwy
- dodaj funkcję
- funkcja usuwania
- funkcja podzielona na dwie lub więcej funkcji
- połączyć dwie lub więcej funkcji w jedną funkcję
- klasa podzielona
- łączyć klasy
- zmień nazwę klasy
Będę argumentować, że każde z wymienionych działań w pewien sposób zmienia zachowanie .
I będę argumentować, że jeśli twoje refaktoryzacja zmieni zachowanie, twoje testy nadal będą w taki sposób, jak upewnisz się, że nic nie zepsułeś.
Być może zachowanie nie zmienia się na poziomie makr, ale celem testów jednostkowych nie jest zapewnienie zachowania makr. To testy integracyjne . Celem testów jednostkowych jest upewnienie się, że poszczególne części, z których zbudujesz swój produkt, nie są zepsute. Łańcuch, najsłabsze ogniwo itp.
Co z tym scenariuszem:
Załóżmy, że masz function bar()
function foo()
wykonuje połączenie z bar()
function flee()
wywołuje również funkcję bar()
Tylko dla odmiany, flam()
dzwoni dofoo()
Wszystko działa wspaniale (przynajmniej najwyraźniej).
Refaktoryzujesz ...
bar()
zostaje przemianowany na barista()
flee()
zmienia się na połączenie barista()
foo()
się nie zmieniło na wezwaniebarista()
Oczywiście, twoje testy dla obu foo()
i flam()
teraz nie.
Może w ogóle nie zdawałeś sobie sprawy z foo()
powołania bar()
. Z pewnością nie zdawał sobie sprawy, że flam()
była zależna bar()
drodze foo()
.
Cokolwiek. Chodzi o to, że twoje testy ujawnią nowo złamane zachowanie obu foo()
i flam()
, stopniowo, podczas pracy nad refaktoryzacją.
Testy pomagają ci dobrze refaktoryzować.
Chyba że nie masz żadnych testów.
To trochę wymyślony przykład. Są tacy, którzy twierdzą, że jeśli zmienia się bar()
przerwy foo()
, to foo()
był zbyt skomplikowany, aby zacząć i powinien zostać zepsuty. Ale procedury mogą wywoływać inne procedury z konkretnego powodu i nie można wyeliminować całej złożoności, prawda? Naszym zadaniem jest zarządzanie rozsądne złożonością.
Rozważ inny scenariusz.
Budujesz budynek.
Zbudujesz rusztowanie, aby upewnić się, że budynek jest poprawnie zbudowany.
Rusztowanie pomaga między innymi zbudować szyb windy. Następnie burzysz rusztowanie, ale szyb windy pozostaje. Zniszczyłeś „oryginalne dzieło”, niszcząc rusztowanie.
Analogia jest niepewna, ale chodzi o to, że nie jest niespotykane budowanie narzędzi, które pomogą Ci zbudować produkt. Nawet jeśli narzędzia nie są trwałe, są przydatne (nawet konieczne). Stolarze cały czas robią przyrządy, czasem tylko do jednej pracy. Następnie rozrywają przyrządy, czasem używając części do budowy innych przyrządów do innych zadań, a czasem nie. Ale to nie sprawia, że przyrządy są bezużyteczne lub zmarnowane.