Jak uniknąć gigantycznych metod klejenia?

21

W mojej obecnej pracy kilkakrotnie miałem za zadanie wyczyścić stary kod. Często kod jest labiryntem, a dane za nim jeszcze bardziej splątane. Łączę rzeczy w ładne, schludne, modułowe metody. Każda metoda robi jedną rzecz i robi to dobrze. Wtedy rzeczy zaczynają iść na południe ...

Niezmiennie kończę na czystym API i nie ma prawdziwego sposobu na połączenie tego wszystkiego razem. Rozwiązaniem było napisanie dużej brzydkiej metody „kleju” (zwykle pełnej instrukcji warunkowych), która ostatecznie wywołuje wszystkie moje „czyste” metody.

Metoda kleju zwykle kończy się zwięzłą wersją plątaniny kodu / danych, którą próbowałem usunąć. Jest ogólnie bardziej czytelny, ale wciąż denerwujący.

Jak mogę uniknąć takich metod? Czy jest to objaw splątanych danych, czy odbicie czegoś, co robię źle?

cmhobbs
źródło
3
Interfejsy API są przeznaczone do użycia. Na podstawie splątanego bałaganu utworzyłeś interfejs API, a następnie ponownie go splątałeś. Może to tylko wymóg biznesowy. Ale dodałeś wartość, ponieważ ktoś inny może przyjść i sprawić, że kolejna funkcja klejenia z łatwością za pomocą twojego API. Nie trzeba załamywać rąk ...
Posłanka Aditya
1
Czy w ogóle rozmawiamy tutaj o przedmiotach, czy po prostu bawimy się wszędzie?
Erik Reppen
3
Nie sądzę, że jest to duplikat tego pytania, mówię nieco bardziej ogólnie (i na większą skalę niż jedna funkcja).
cmhobbs
1
erik - Mówię tutaj o przedmiotach i metodach. Wziąłem kilka warunkowych komunikatów i zamieniłem je w interfejsy API. Problem pojawia się, gdy nadszedł czas, aby wywołać interfejs API. Pierwsza odpowiedź tutaj może być dokładnie tym, czego szukam.
cmhobbs
2
Jak, u licha, jest to duplikat?
MattDavey

Odpowiedzi:

12

Dam ci nasze doświadczenie w refaktoryzacji LedgerSMB. Podjęliśmy decyzję, aby robić rzeczy inaczej na początku i nadal robimy dokładnie to, co opisujesz, ale bez wielu metod klejenia (mamy kilka metod klejenia, po prostu niewiele).

Życie z dwoma bazami kodów

LedgerSMB przetrwał z dwiema bazami kodów przez około 5 lat i minie jeszcze kilka, zanim stara baza kodów zostanie wyeliminowana. Stara baza kodów to prawdziwy horror do zobaczenia. Zły projekt bazy danych, Perl konstruuje jak IS->some_func(\%$some_object);wraz z kodem, który pokazuje dokładnie, dlaczego czasami używana jest metafora spaghetti (ścieżki wykonywania wiją się między modułami i z powrotem, oraz między językami, bez wierszyka ani powodu). Nowa baza kodów unika tego, przenosząc zapytania db do procedur przechowywanych, mając czystszą strukturę do obsługi żądań i wiele więcej.

Pierwszą rzeczą, którą postanowiliśmy zrobić, była próba refaktoryzacji moduł po module. Oznacza to przeniesienie całej funkcjonalności z określonego obszaru do nowego modułu, a następnie podpięcie starego kodu do nowego modułu. Jeśli nowy interfejs API jest czysty, to nie jest wielka sprawa. Jeśli nowy interfejs API nie jest czymś owłosionym, jest to zaproszenie do nieco cięższej pracy nad nowym interfejsem API ....

Po drugie, wiele razy nowy kod musi uzyskać dostęp do logiki starego kodu. Należy tego unikać w możliwym zakresie, ponieważ prowadzi to do brzydkich metod klejenia, ale nie zawsze można tego uniknąć. W takim przypadku metody klejenia należy zminimalizować i unikać w możliwym zakresie, ale stosować w razie potrzeby.

Aby ta praca działała, musisz zobowiązać się do przepisania wszystkich funkcji w określonym obszarze. Jeśli możesz na przykład przepisać cały kod śledzenia informacji o kliencie na raz, oznacza to, że kod, który wywołuje ten kod ze starego kodu, nie jest trudny w obsłudze, a wysyłanie do starego kodu z nowego kodu jest zminimalizowane.

Po drugie, jeśli masz rozsądne abstrakty na swoim miejscu, powinieneś być w stanie wybrać, który poziom interfejsu API ma być wywoływany i jak utrzymać go w czystości. Należy jednak pomyśleć o przepisaniu fragmentów wywołujących interfejs API, aby były one również nieco czystsze.

Istnieje wiele obszarów narzędzi biznesowych, które są nieredukowalnie złożone. Nie możesz pozbyć się całej złożoności. Ale możesz nim zarządzać, koncentrując się na czystych interfejsach API, które wykonują to, co musisz zrobić, oraz modułach, które konstruktywnie wykorzystują ten interfejs API. Klej powinien być ostatecznością dopiero po rozważeniu, że przepisanie pozostałej części kodu może być szybsze.

Chris Travers
źródło
Myślę, że mogłeś trafić w gwóźdź. Przyczyną istnienia kleju może być kod wywołujący interfejs, który utworzyłem. Zaczekam na kilka odpowiedzi, aby zobaczyć, czy coś przegapimy, ale wierzę, że to całkiem dobrze podsumowuje.
cmhobbs
1
„ścieżki wykonywania wiją się między modułami iz powrotem, a także między językami, bez rymu i powodu” - to przypomina mi również niektóre współczesne praktyki OO.
user253751
8

Wygląda na to, że to, co zrobiłeś, zostało poplątane w zbłąkaną bazę kodową i stworzyło piękną modułową bazę kodową.

Niezmiennie kończę na czystym API i nie ma prawdziwego sposobu na połączenie tego wszystkiego razem. Rozwiązaniem było napisanie dużej brzydkiej metody „kleju” (zwykle pełnej instrukcji warunkowych), która ostatecznie wywołuje wszystkie moje „czyste” metody.

Dzięki kodowi proceduralnemu (nawet jeśli jest zamaskowany jako OO), zawsze kończysz się zdefiniowaniem jakiegoś sekwencyjnego przepływu pracy, często opisywanego złożonymi gałęziami warunkowymi. Podejrzewam, że ta proceduralna natura kodu sprawia, że ​​czujesz, że coś jest nie tak. Nie musi to być złą rzeczą, a praca ze starszym kodem może być całkowicie nieunikniona

MattDavey
źródło
6

Powinieneś wyczyścić dużą brzydką metodę kleju w ten sam sposób, w jaki wyczyściłeś oryginalną bazę kodu. Podziel go na czyste, modułowe metody. Prawdopodobnie masz grupy wierszy kodu, które wykonują pewne zadania, dzieląc te wiersze na metody, jeśli dzielisz niektóre zmienne, możesz rozważyć umieszczenie wspólnych zmiennych i nowych metod w jednej klasie.

Palisada
źródło
2
Nie dostajesz więc drzewa kleju?
Pieter B
3
@PieterB może, ale łatwiej jest wyodrębnić różne zależności, gdy masz różne zadania różnymi metodami. Po wyodrębnieniu nowych metod można wykonać inną przepustkę refaktoryzacji.
Paling
1

Zasadniczo dodajesz warstwy abstrakcji, dopóki nie spojrzy poprawnie na każdą warstwę wykonaną samodzielnie . Paradoksalną rzeczą w abstrakcji jest to, że dodajesz złożoność, aby ją zmniejszyć, ponieważ kiedy czytasz abstrakcyjny kod, zajmujesz się tylko jedną warstwą na raz. Jeśli każda warstwa jest wystarczająco mała, aby ją łatwo zrozumieć, nie ma znaczenia, na ilu warstwach spoczywa.

To również sprawia, że ​​abstrakcje są trudne do napisania. Nawet coś tak prostego, jak ołówek , pochyla umysł, jeśli spróbujesz zatrzymać wszystkie warstwy abstrakcji w swojej głowie. Kluczem jest zdobycie jednej warstwy tak, jak lubisz, co zrobiłeś, a następnie zapomnienie o złożoności leżącej u podstaw tej warstwy i zrobienie tego samego na następnym poziomie.

Karl Bielefeldt
źródło
0

Wygląda na to, że refaktoryzujesz interfejs API, po prostu myśląc o implementacji interfejsu API, ale nie zastanawiając się wystarczająco nad kodem korzystającym z interfejsu API, czyli „kodu kleju”, o którym mówisz.

Jeśli to prawda, możesz spróbować zacząć od drugiego końca. Przepisz rzeczy, które mogą stać się Twoim brzydkim kodem kleju, i utwórz kilka jeszcze nie zaimplementowanych interfejsów, które staną się Twoim API w tym procesie. Nie myśl jeszcze zbyt dużo o faktycznej implementacji tego API - jest OK, jeśli masz przeczucie, że możesz to zrobić. I dopiero wtedy przepisz labirynt kodu, aby był zgodny z tym interfejsem API. Oczywiście w tym procesie zostaną wprowadzone pewne zmiany w interfejsie API i kodzie kleju, ale powinien on lepiej do siebie pasować.

Hans-Peter Störr
źródło