Co to jest refaktoryzacja, a co tylko modyfikuje kod?


81

Wiem, że refaktoryzacja to „zmiana struktury programu tak, aby funkcjonalność się nie zmieniała”. Rozmawiałem z kilkoma chłopakami pracuję z na mój projekt końcowej roku na Uniwersytecie i byłem zaskoczony, że mają one znacznie bardziej rozległy (z braku lepszego słowa) widok refactoring.

Uważam, że refaktoryzacja polega na wyodrębnianiu metod i zmianie nazw klas. Zaproponowali także takie rzeczy, jak zmiana struktur danych (takich jak Java LinkedListna an ArrayList), zmiana algorytmów (użycie sortowania przez scalanie zamiast sortowania bąbelkowego), a nawet przepisywanie dużych fragmentów kodu jako refaktoryzacji.

Byłem całkiem pewien, że się mylili, ale nie byłem w stanie podać dobrego powodu, ponieważ to, co sugerowali, zmieniło program (i prawdopodobnie poprawiło) bez zmiany jego zachowania. Czy mam rację, a co ważniejsze, dlaczego?

Odpowiedzi:


77

„Refaktoryzacja: ulepszanie projektu istniejącego kodu” Martina Fowlera to prawdopodobnie odniesienie:

Refaktoryzacja to kontrolowana technika ulepszania projektu istniejącej bazy kodu. Jej istotą jest zastosowanie szeregu małych transformacji zachowujących zachowanie, z których każda jest „zbyt mała, aby była warta zrobienia”. Jednak łączny efekt każdej z tych przemian jest dość znaczący. Wykonując je małymi krokami, zmniejszasz ryzyko wprowadzenia błędów. Unikniesz również awarii systemu podczas przeprowadzania restrukturyzacji - co pozwala na stopniową refaktoryzację systemu w dłuższym okresie czasu.

Refaktoryzacja idzie w parze z testowaniem jednostkowym. Napisz testy przed refaktoryzacją, a wtedy uzyskasz poziom zaufania do refaktoryzacji (proporcjonalny do pokrycia testów).

Dobra referencja: Informacje o refaktoryzacji


16
Cytat Fowlera jest oczywiście istotny i tak, idzie w parze z testami jednostkowymi ... Ale czy to naprawdę odpowiada na zadane pytania: Czy wspomniane przykłady są refaktoryzacją, czy tylko modyfikacją kodu? Kto ma rację, OP czy jego koledzy i dlaczego?
Jonik

36

Fowler rysuje czystą linię między zmianami w kodzie, które to robią, a tymi, które tego nie robią, wpływają na jego zachowanie. Tych, którzy tego nie robią, nazywa „refaktoryzacją”. To jest istotna różnica, bo jeśli dzielimy naszą pracę w refactoring i non-refactoring działania modyfikacji kodu (Fowler nazywa ją „nosi różne kapelusze”), możemy zastosować różne, cel-właściwe techniki.

Jeśli dokonujemy refaktoryzacji lub modyfikacji kodu zachowującej zachowanie:

  • wszystkie nasze testy jednostkowe powinny przejść przed i po modyfikacji
  • nie powinniśmy modyfikować żadnych testów ani pisać nowych
  • oczekujemy czystszego kodu, gdy skończymy
  • nie oczekujemy nowego zachowania

Jeśli dokonujemy modyfikacji kodu zmieniającej zachowanie:

  • oczekujemy nowego zachowania
  • powinniśmy napisać nowe testy
  • możemy otrzymać brudniejszy kod, gdy skończymy (i powinniśmy go następnie zreformować)

Jeśli stracimy z oczu to rozróżnienie, wówczas nasze oczekiwania dotyczące każdego zadania modyfikacji kodu są niejasne i złożone, a przynajmniej bardziej zagmatwane i bardziej złożone, niż gdybyśmy byli tego świadomi. Dlatego ważne jest słowo i jego znaczenie.


3
+1, dokładnie. Szczególnie uzasadnienie, które podajesz; niejasne oczekiwania. Pisząc własną odpowiedź, miałem to na uwadze, nawet jeśli nie udało mi się zapisać jej tak starannie :)
Jonik

18

Aby wyrazić swój pogląd:

Małe, przyrostowe zmiany, które pozostawiają kod w lepszym stanie niż został znaleziony

Zdecydowanie tak: zmiany „kosmetyczne”, które nie są bezpośrednio związane z funkcjami (tj. Nie są rozliczane jako żądanie zmiany).

Zdecydowanie nie: przepisywanie dużych fragmentów w oczywisty sposób narusza część „małych, przyrostowych”. Refaktoryzacja jest często używana jako przeciwieństwo przepisywania: zamiast robić to ponownie, ulepsz istniejące.

Zdecydowanie być może: zastępowanie struktur danych i algorytmów jest przypadkiem granicznym. Decydującą różnicą w tym przypadku IMO są małe kroki: bądź gotowy do dostarczenia, bądź gotowy do pracy nad inną sprawą.


Przykład: Wyobraź sobie, że masz moduł Report Randomizer, który jest spowolniony przez użycie wektora. Zaprofilowałeś, że wstawianie wektorów jest wąskim gardłem, ale niestety moduł opiera się na pamięci ciągłej w wielu miejscach, więc podczas korzystania z listy rzeczy po cichu się psują.

Przepisanie oznaczałoby wyrzucenie modułu z budynku na lepszy i szybszy od zera, po prostu wybranie kilku elementów ze starego. Lub napisać nowy rdzeń, a następnie dopasować go do istniejącego okna dialogowego.

Refaktoryzacja oznaczałaby podjęcie małych kroków w celu usunięcia arytmetyki wskaźnika, tak aby przełącznik. Może nawet utworzysz funkcję narzędzia zawijającą arytmetykę wskaźnika, zastępując bezpośrednią manipulację wskaźnikiem wywołaniami tej funkcji, a następnie przełącz się na iterator, aby kompilator narzekał na miejsca, w których arytmetyka wskaźnika jest nadal używana, a następnie przełącz się na a list, a następnie usuń funkcja ultility.


Pomysł polega na tym, że kod sam się pogarsza. Podczas naprawiania błędów i dodawania funkcji jakość spada małymi krokami - znaczenie zmiennej nieznacznie się zmienia, funkcja otrzymuje dodatkowy parametr, który przerywa izolację, pętla staje się nieco skomplikowana itp. Żaden z nich nie jest prawdziwym błędem, możesz Nie mów liczby linii, która sprawia, że ​​pętla jest złożona, ale szkodzi czytelności i konserwacji.

Podobnie, zmiana nazwy zmiennej lub wyodrębnienie funkcji nie są same w sobie namacalnymi ulepszeniami. Ale razem walczą z powolną erozją.

Jak ściana z kamyków, w której codziennie upadamy na ziemię. I codziennie jeden przechodzień podnosi go i odkłada.


12

Mając na uwadze definicję Martina Fowlera,

Refaktoryzacja to zdyscyplinowana technika restrukturyzacji istniejącego korpusu kodu, zmiany jego wewnętrznej struktury bez zmiany jego zewnętrznego zachowania.

... Myślę, że masz wyraźną rację.

Zasugerowali również takie rzeczy, jak zmiana struktur danych (takich jak Java LinkedList na ArrayList), zmiana algorytmów (przy użyciu sortowania przez scalanie zamiast sortowania bąbelkowego), a nawet przepisywanie dużych fragmentów kodu jako refaktoryzacji.

Zmiana algorytmu na coś znacznie szybszego oczywiście nie jest refaktoryzacją, ponieważ zmienia się zachowanie zewnętrzne! (Z drugiej strony, jeśli efekt nigdy nie jest zauważalny, być może można by to nazwać refaktoryzacją - a także przedwczesną optymalizacją. :-)

To moje zwierzę irytacja; denerwujące jest, gdy ludzie używają tego terminu niechlujnie - spotkałem nawet takich, którzy mogliby przypadkowo użyć refaktoryzacji w zasadzie do jakiejkolwiek zmiany lub naprawy. Tak, to modne i fajne modne hasło iw ogóle, ale nie ma nic złego w prostych, starych terminach, takich jak zmiana , przepisanie lub poprawa wydajności . Powinniśmy ich używać, gdy jest to stosowne, i zarezerwować refaktoryzację w przypadkach, gdy naprawdę ulepszasz wewnętrzną strukturę swojego oprogramowania. Szczególnie w zespole programistów posiadanie wspólnego języka do dokładnego omawiania swojej pracy ma znaczenie.


3
Nie jestem pewien, że uczynienie kod szybciej kwalifikuje się jako zmianę w zachowaniu zewnętrznym ...
GalacticCowboy

2
Tak, rozumiem twój punkt widzenia, myślę, że to zależy od kąta, pod jakim na to patrzysz. :) W każdym razie IMO, programując należy zwrócić uwagę na to, jaki "kapelusz" nosisz w danym momencie, czyli co dokładnie próbujesz osiągnąć. Innymi słowy, powinieneś świadomie rozdzielać podczas dodawania / naprawiania funkcji, podczas refaktoryzacji i optymalizacji (poprawiania wydajności). IIRC, Fowler również mówi o tym w swojej ostatecznej książce o refaktoryzacji.
Jonik

1
Jeśli chodzi o część dotyczącą zmiany zewnętrznej, moglibyśmy przeformułować tak: [...] bez zmiany jej zachowania zewnętrznego, jeśli zachowanie ma znaczenie. Jeśli wydajność jest ważna w obu przypadkach (szybciej lub wolniej), nie wykonuj zmiany, która może na nią wpłynąć w ramach fazy „refaktoryzacji”.
Loki

11

Jeśli interfejs do fragmentu kodu ulegnie zmianie, uważam to za coś więcej niż refaktoryzację.

Typowy przypadek refaktoryzacji to

  • „Och, wszystkie moje testy jednostkowe są uruchamiane, ale myślę, że mój kod można by poprawić”
  • Zmień kod, aby był bardziej czytelny / bardziej przejrzysty / wydajny
  • Uruchom ponownie testy jednostkowe (bez zmiany testów) i sprawdź, czy nadal działają

Oznacza to, że termin refaktoryzacja odnosi się do omawianego interfejsu. tj. mógłbyś refaktoryzować kod za jednym interfejsem, jednocześnie bardziej szeroko zmieniając kod innego na niższym poziomie (może to rozróżnienie powoduje zamieszanie między tobą a twoimi kolegami?)


7

Myślę, że masz rację, ale spór o znaczenie słowa nie jest szczególnie interesujący ani produktywny.


6
Zwykle bym się z tym zgadzał, ale dyskusja pojawiła się, gdy przeglądaliśmy napisane przez nas dokumenty i myślę, że dobrze jest, gdy mówimy o tym samym, gdy używamy tych samych słów.
David Johnstone

4

http://en.wikipedia.org/wiki/Code_refactoring

Refaktoryzacja kodu to proces zmiany wewnętrznej struktury programu komputerowego bez modyfikowania jego zewnętrznego zachowania funkcjonalnego lub istniejącej funkcjonalności, w celu poprawy wewnętrznych niefunkcjonalnych właściwości oprogramowania, na przykład w celu poprawy czytelności kodu, uproszczenia struktury kodu, zmiany kodu aby przestrzegać określonego paradygmatu programowania, poprawić łatwość konserwacji, poprawić wydajność lub poprawić rozszerzalność.

Zgadzam się, że kod refaktoryzacji obejmuje łamanie istniejącego kodu. Po prostu upewnij się, że masz testy jednostkowe, aby nie wprowadzać żadnych błędów, a reszta kodu się kompiluje. Korzystanie z narzędzi do refaktoryzacji, takich jak Resharper for C #, sprawia, że ​​jest to takie proste!

  • Uczynienie kodu bardziej zrozumiałym
  • Czyszczenie kodu i porządkowanie go
  • Usuwanie kodu! Należy usunąć zbędny, nieużywany kod i komentarze
  • Poprawa wydajności
  • Stworzenie czegoś bardziej ogólnego. Zacznij od najprostszej możliwej rzeczy, a następnie przeprowadź jej refaktoryzację, aby ułatwić testowanie / izolowanie lub generyczną, aby mogła działać na różne sposoby poprzez polimorfizm
  • Utrzymywanie kodu w stanie SUCHYM - nie powtarzaj tego samego, więc sesja refaktoryzacji może wymagać wzięcia kilku powtórzeń kodu i przekształcenia go w jeden komponent / klasę / moduł.

3

Nie zgadzam się :

W inżynierii oprogramowania „refaktoryzacja” kodu źródłowego oznacza ulepszanie go bez zmiany jego ogólnych wyników [...]

Znasz już dokładniejsze terminy używane dla podzbiorów refaktoryzacji i tak, jest to bardzo ogólne określenie.


1

Myślę, że nikt nie może skorzystać na zbyt mocnej definicji terminu „refaktoryzacja”. Granica między tym, jak to postrzegasz, a twoimi współpracownikami jest niewyraźna i może być bliższa ich lub twojemu poglądowi w zależności od wielu faktów. Ponieważ jest dynamiczny, spróbujmy go zdefiniować. Przede wszystkim zdefiniuj granice systemu lub podsystemu, który próbujesz refaktoryzować.

Jeśli jest to metoda, zachowaj nazwę, argumenty wejściowe, typ zwracanej wartości i ewentualnie poprawione wyrzucanie instrukcji. Zastosuj wszystkie zmiany wewnątrz metody bez zmiany sposobu, w jaki jest ona postrzegana na zewnątrz.

Jeśli zmienisz klasę, naprawisz jej publiczny interfejs API i użyjesz zmiennych zmiany nazwy, wyodrębnij metody i wszystkie inne dostępne techniki zmienią klasę, aby była bardziej czytelna i / lub wydajniejsza.

Jeśli część kodu, którą refaktoryzujesz, jest pakietem lub modułem, dokonaj refaktoryzacji wewnątrz niego, ewentualnie zmień nazwę klas, usuń, wprowadź interfejsy, wypchnij / wciągnij kod do super / podklas.


0

Refaktoryzacja = poprawa wymagań niefunkcjonalnych przy zachowaniu niezmienionych wymagań funkcjonalnych.

Wymagania niefunkcjonalne = modułowość, testowalność, łatwość utrzymania, czytelność, oddzielenie problemów, zasady liskov i tak dalej ...

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.