Słyszę dużo o skróceniu metod i słyszałem, jak wielu programistów twierdzi, że użycie tagów #region w metodzie jest pewnym znakiem, że jest ona zbyt długa i powinna zostać przekształcona w wiele metod. Wydaje mi się jednak, że istnieje wiele przypadków, w których oddzielenie kodu za pomocą znaczników #region w metodzie jest najlepszym rozwiązaniem do refaktoryzacji na wiele metod.
Załóżmy, że mamy metodę, której obliczenia można podzielić na trzy dość odrębne fazy. Co więcej, każdy z tych etapów jest istotny tylko dla obliczeń tej metody, więc wyodrębnienie ich w nowe metody nie powoduje ponownego użycia kodu. Jakie są zatem zalety wyodrębnienia każdej fazy z jej własnej metody? O ile mogę stwierdzić, zyskujemy tylko pewną czytelność i osobny zmienny zakres dla każdej fazy (co pomoże zapobiec przypadkowemu przerwaniu modyfikacji przez konkretną fazę).
Jednak oba z nich można osiągnąć bez wyodrębniania każdej fazy we własnej metodzie. Tagi regionalne pozwalają nam zwinąć kod do postaci, która jest równie czytelna (z dodatkową korzyścią, że nie musimy już zostawiać swojego miejsca w tym pliku, jeśli zdecydujemy się rozwinąć i zbadać kod), i po prostu zawijamy każdą fazę w {}
tworzy własny zakres do pracy.
Zaletą robienia tego w ten sposób jest to, że nie zanieczyszczamy zakresu na poziomie klasy za pomocą trzech metod, które w rzeczywistości są istotne tylko dla wewnętrznych mechanizmów czwartej metody. Natychmiastowe przekształcenie długiej metody w szereg krótkich metod wydaje mi się być ponownym użyciem kodu równoważnym z przedwczesną optymalizacją; wprowadzasz dodatkową złożoność, aby rozwiązać problem, który w wielu przypadkach nigdy nie powstaje. Zawsze możesz później wyodrębnić jedną z faz we własnej metodzie, jeśli pojawi się możliwość ponownego użycia kodu.
Myśli?
źródło
#region
tagów, ale całkowicie wyłączam zwijanie kodu w Visual Studio. Nie lubię kodu, który próbuje się przede mną ukryć.Odpowiedzi:
Jedyne, o co powinieneś się troszczyć, to to, aby kod był użyteczny, a nie wielokrotnego użytku. Małpa może przekształcić użyteczny kod w kod wielokrotnego użytku, jeśli w ogóle trzeba wykonać jakieś transformacje.
Argument „potrzebuję tego tylko tutaj” jest słaby, mówiąc grzecznie. Technikę, którą opisujesz, często określa się mianem techniki nagłówków i na ogół nie ma do niej pretensji.
Również istotny: przemyślenia Jeffa Atwoodsa na temat zwijania kodu
źródło
Problemem nie są regiony w metodach, ale przypadek, gdy istnieje potrzeba (a nawet zastosowanie) regionów w metodach.
Po tym, jak musiałem debugować wiele 200-300 metod liniowych, mogę powiedzieć, że nie chciałbym tego na nikim. Jeśli piszesz i dbasz o swój własny kod, to w porządku - rób co chcesz. Ale gdy ktoś na to spojrzy, natychmiast powinno być jasne, co robi metoda.
Nie, to nie wystarczy. Podziel to na regiony, jak chcesz, ale wciąż kręcę głową, myśląc o tym.
Jeśli włamiesz się na metody, uzyskasz wiele korzyści:
///
) i wprowadź opis, parametry, typ zwracany, a takżeexception
,example
,remarks
węzły na szczegóły tych scenariuszy. Tam właśnie idzie, po to jest!{}
, ale czy muszę sprawdzać każdą metodę, aby upewnić się, że nie „oszukujesz”? CzydodgyObject
używam tego tylko dlatego, że był używany w tym regionie, czy też pochodzi z innego miejsca? Gdybym był według własnej metody, z łatwością mógłbym zobaczyć, czy został on przekazany mi, czy też sam go stworzyłem (a raczej czy to Ty go stworzyłeś).TurnThingsUpsideDown
metoda zawiodła - prawdopodobnie zawodzi podczas obracania złej rzeczy” jest o wiele lepsza niż „Och,DoEverything
metoda zawiodła - mogło być 50 różnych rzeczy i będę musiał kopać przez godzinę, aby ją znaleźć” .To wszystko zanim pojawią się jakiekolwiek wątpliwości dotyczące możliwości ponownego użycia lub poprawy. Właściwie wyodrębnione metody oczywiście ułatwiają ponowne użycie, a także pozwalają łatwo zastąpić metodę, która nie działa (zbyt wolno, zbyt błędnie, zmieniona zależność itp.). Czy kiedykolwiek próbowałeś refaktoryzować którąś z tych wielkich metod? Nie ma sposobu, aby wiedzieć, że to, co zmienisz, nie wpłynie na nic innego.
Proszę poprawnie zawrzeć logikę w rozsądnych metodach. Wiem, że łatwo im wymknąć się spod kontroli, a argumentowanie, że w tym momencie nie warto przeprojektowywać, to kolejna kwestia. Ale musisz zaakceptować fakt, że nie ma szkody ani przynajmniej potencjalnej korzyści z posiadania odpowiednio zamkniętych, czysto napisanych i po prostu zaprojektowanych metod.
źródło
Jeśli twoja metoda jest na tyle złożona, że można ją podzielić na trzy odrębne fazy, to nie tylko powinny to być trzy osobne metody ... ale twoja metoda powinna być w rzeczywistości osobną klasą dedykowaną do tego obliczenia.
„Ale wtedy moja klasa będzie miała tylko jedną metodę publiczną!”
Masz rację i nie ma w tym nic złego. Zasada zasady pojedynczej odpowiedzialności polega na tym, że twoja klasa robi jedno .
Twoja klasa może wywoływać kolejno każdą z trzech metod i zwracać wynik. Jedna sprawa.
Jako bonus, teraz twoja metoda jest testowalna, ponieważ nie jest już metodą prywatną.
Ale nie wspominając o jednej klasie; w rzeczywistości powinieneś mieć cztery : po jednym dla każdego z elementów twojej metody, plus jeden, który bierze każdą z trzech jako zależność i wywołuje je we właściwej kolejności, zwracając wynik.
To sprawia, że Twoje zajęcia są jeszcze bardziej testowalne. Ułatwia także zmianę jednego z tych trzech elementów. Wystarczy napisać nową klasę dziedziczącą po starej i zastąpić zmienione zachowanie. Lub stwórz interfejs dla zachowania każdego z twoich trzech elementów; a następnie, aby je zastąpić, po prostu napisz nową klasę implementującą ten interfejs i zastąp go starą w konfiguracji kontenera wstrzykiwania zależności.
Co? Nie używasz zastrzyku zależności? Cóż, prawdopodobnie nie widziałeś jeszcze takiej potrzeby, ponieważ nie przestrzegasz zasady pojedynczej odpowiedzialności. Kiedy zaczniesz, przekonasz się, że najłatwiejszym sposobem nadania zależności każdej klasie jest użycie kontenera DI.
EDYCJA :
OK, odłóżmy na bok pytanie o refaktoryzację. Celem
#region
jest ukrycie kodu , co oznacza głównie kod, który nie musi być edytowany w żadnych normalnych okolicznościach. Wygenerowany kod jest najlepszym przykładem. Powinien być ukryty w regionach, ponieważ zwykle nie trzeba go edytować. W razie potrzeby rozszerzenie regionu daje ci trochę ostrzeżenia w stylu „Oto smoki”, aby upewnić się, że wiesz, co robisz.Dlatego powiedziałbym, że nie
#region
powinien być stosowany w środku metody. Jeśli otworzę metodę, chcę zobaczyć kod. Użycie #region daje mi kolejny poziom ukrywania, którego nie potrzebuję, a to staje się irytujące: rozwijam metodę ... i wciąż nie widzę żadnego kodu.Jeśli martwisz się, że ludzie widzą strukturę metody (i odmawiasz jej refaktoryzacji), dodaj komentarze banerowe w następujący sposób:
Pomogą one komuś przeglądającemu kod zobaczyć poszczególne części twojej metody bez konieczności zajmowania się rozwijaniem i zwijaniem
#region
tagów.źródło
ctrl+n
Myślę, że przykład, który podajesz w swoim argumencie, dotyczy mniej YAGNI (nie będziesz go potrzebował), a więcej o pisaniu kodu jakości, który można łatwo utrzymać.
Na najwcześniejszych kursach programistycznych dowiadujemy się, że jeśli metoda ma 700 LOC, jest prawdopodobnie zbyt duża i na pewno byłoby ogromnym bólem, aby spróbować i debugować.
Po drugie, jeśli napiszę metody A, B i C, które są używane tylko w ramach metody D, to mogę łatwiej testować jednostki AB i C niezależnie od D, pozwalając na bardziej niezawodne testy jednostkowe we wszystkich 4 metodach.
źródło
To dwie zalety. Ale dla mnie najważniejsza jest debuggowanie . Jeśli musisz prześledzić ten kod, o wiele łatwiej jest przejść przez dwie z trzech sekcji i prześledzić tylko tę, na której ci zależy. Umożliwia to refaktoryzacja na mniejsze metody; tagi regionu nie.
źródło
ctrl-f10
- biegnij do kursoraMoją osobistą zasadą jest to, że jeśli metoda jest dłuższa niż jeden ekran, powiedzmy 40 linii, to jest za długa. Jeśli klasa jest dłuższa niż 500 linii, jest za duża. Czasami łamię te zasady, czasami nawet dużą wielokrotność, ale generalnie żałuję tego w ciągu roku.
Nawet metoda składająca się z 20 do 30 linii w większości przypadków jest nieco długa. Powiedziałbym więc, że użycie regionów w metodzie jest zwykle złym pomysłem, po prostu dlatego, że prawie nigdy nie miałoby sensu zwinięcie regionu zawierającego od 20 do 30 linii w wiele podregionów.
Podział funkcji, które są długie według tej definicji, ma co najmniej dwie zalety:
Możesz nazwać to, co robią te podfunkcje, co wyjaśnia twoje obecne myślenie i upraszcza zadanie późniejszych opiekunów.
Rozkład na mniejsze funkcje zmusza do przekazania tylko tych parametrów, których potrzebują mniejsze funkcje, więc zakres wymaganych i modyfikowanych danych jest bardzo jasny. Zakłada się, że nie uzyskujesz dostępu i nie modyfikujesz stanu obiektu w stu różnych funkcjach liści, co również odradzam.
Z mojego doświadczenia wynika, że reguły te tworzą kod, który jest łatwiejszy do napisania bez popełniania typowych błędów i może być później zrozumiany i zmodyfikowany bez przerywania subtelnych zachowań. Nie ma znaczenia, czy osobą, która musi zrozumieć / zmodyfikować kod, jest ktoś inny, czy ja po tym, jak spałem i zapomniałem o wszystkim.
Dlatego uważam regiony w metodach za „zapach kodu” wskazujący, że stosowane są niezrównoważone praktyki. Jak zawsze mogą istnieć wyjątki, ale zdarzają się tak rzadko, że prawie nie warto o nich dyskutować.
źródło
Znowu zaczynamy ... Jest wiele innych tematów na temat programistów, którzy zakończyli tę samą dyskusję.
Wskazujesz kilka bardzo interesujących argumentów związanych z krótkimi funkcjami. To nie jest popularne stwierdzenie, ale jestem z tobą w tej sprawie . Po wcześniejszym omówieniu mam wrażenie, że dyskusja sprowadza się zwykle do tego, czy skupiasz się przede wszystkim na właściwej enkapsulacji, czy też maksymalnie rozwijasz programowanie testowe. To są dwaj główni pretendenci do czegoś, co wydaje się zbliżać do kolejnej świętej wojny, i obawiam się, że jesteśmy po stronie słabej mocy.
Jednak ...
Moim zdaniem rozwiązaniem zdecydowanie nie jest owijanie „faz” w regiony. Składanie kodu służy do zamiatania kodu pod dywan. Zostaw kod tam, gdzie jest, może przypominać, że możesz to zmienić później. Odnosząc to do argumentu w pytaniu: jak będziesz w stanie zobaczyć możliwość ponownego użycia kodu, jeśli nie zobaczysz żadnego kodu? Długie segmenty kodu powinny być dokładnie tym, cierniem w oku, co sprawia, że chcesz go zmienić.
Jako odpowiedni zamiennik dla regionów sugeruję stosowanie akapitów kodu, ponieważ Alex Papadimoulis nazywa je w komentarzu . Być może już używasz podobnego podejścia. Są to po prostu bloki kodu z nagłówkiem komentarza, wyjaśniające, co robi cały blok. Masz czytelność funkcji, ale możesz używać normalnie rozmieszczonych zdań w języku angielskim i nie tracisz żadnej enkapsulacji.
źródło
Chociaż zgadzam się, że zwykle lepiej jest refaktoryzować kod niż użyć
#region
, nie zawsze jest to możliwe.Na poparcie tego stwierdzenia podam przykład.
Łatwo jest dostosować regiony, aby zmienić kolejność lub rozszerzyć, gdy do klasy zostaną dodane pola dodatkowe. W każdym razie wymagane są komentarze, aby szybko zobaczyć, jak powinno wyglądać zamówienie. A przede wszystkim: nie rozumiem, w jaki sposób refaktoryzacja poprawi czytelność w tym przypadku.
Te wyjątki są jednak rzadkie. Zwykle można refaktoryzować, jak mówi wiele innych odpowiedzi.
źródło
Nie sądzę, aby istniała jedna odpowiedź na pytanie, dlaczego niektóre osoby lub zespoły decydują się nie używać,
#region
ale gdybym musiał wymienić kilka, byłoby to najważniejsze powody, które widziałem.źródło