Czy powinniśmy wyeliminować zmienne lokalne, jeśli możemy?

95

Na przykład, aby utrzymać procesor włączony w Androidzie, mogę użyć takiego kodu:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

Ale myślę, że lokalne zmienne powerManageri wakeLockmogą być wyeliminowane:

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

podobna scena pojawia się w widoku alertu iOS, np .: z

UIAlertView *alert = [[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil];
[alert show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

do:

[[[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil] show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

Czy dobrą praktyką jest eliminowanie zmiennej lokalnej, jeśli jest ona używana tylko raz w zakresie?

grgrr
źródło
95
Niekoniecznie. Czasami sprawia, że ​​kod staje się bardziej przejrzysty przy użyciu zmiennych jednorazowych, aw większości przyzwoitych języków programowania koszty tych dodatkowych zmiennych są bardzo niewielkie (jeśli w ogóle).
Robert Harvey
75
Utrudnia także przechodzenie przez kod z debuggerem. Musisz także upewnić się (w zależności od języka), że pierwsze wyrażenie nie ma wartości NULL ani błędu.
GrandmasterB
78
Nie, nie jest. W rzeczywistości dobrą praktyką jest wprowadzanie zmiennych lokalnych w celu przerwania łańcuchów wywołań metod. Wygenerowany kod maszynowy prawdopodobnie będzie identyczny, a kod źródłowy jest prawie na pewno bardziej czytelny, a zatem lepszy.
Kilian Foth
48
Spróbuj tego. Teraz podłącz debugger i spróbuj zobaczyć stan PowerManager lub WakeLock. Uświadom sobie swój błąd. Kiedyś myślałem tak samo („co jest z tymi wszystkimi lokalnymi mieszkańcami?”), Aż musiałem spędzać większość czasu na badaniu kodu.
Faerindel
42
Nawet w twoim przykładzie „wyeliminowana” wersja ma pasek przewijania i nie mieści się całkowicie na moim ekranie, co znacznie utrudnia czytanie.
Erik

Odpowiedzi:

235

Kod jest odczytywany znacznie częściej niż jest napisany, więc powinieneś litować się nad biedną duszą, która będzie musiała czytać kod za sześć miesięcy (może to być Ty) i dążyć do jak najczystszego, najłatwiejszego do zrozumienia kodu. Moim zdaniem pierwsza forma ze zmiennymi lokalnymi jest znacznie bardziej zrozumiała. Widzę trzy akcje w trzech liniach, a nie trzy akcje w jednej linii.

A jeśli uważasz, że optymalizujesz cokolwiek, pozbywając się zmiennych lokalnych, nie jesteś. Nowoczesny kompilator umieści powerManagerw rejestrze 1 , niezależnie od tego, czy używana jest zmienna lokalna, czy nie, wywołanie newWakeLockmetody. To samo dotyczy wakeLock. W obu przypadkach otrzymujesz ten sam skompilowany kod.

1 Jeśli miałeś dużo interweniującego kodu między deklaracją a użyciem zmiennej lokalnej, może pójść na stos, ale jest to drobny szczegół.

Tony BenBrahim
źródło
2
Nie możesz wiedzieć, czy jest dużo kodu, optymalizator może wbudować niektóre funkcje ... W przeciwnym razie, uzgodnione, najpierw dążenie do czytelności jest właściwym posunięciem.
Matthieu M.
2
Cóż, myślę odwrotnie, jeśli widzę, że zmienne lokalne są inicjalizowane wiele razy dla każdej funkcji, w której są używane, zamiast być atrybutami, kiedy to możliwe, poszukaj, dlaczego w zasadzie dokładnie przeczytam wiersze i porównuję je tylko do upewnij się, że są takie same. Więc będę marnować czas.
Walfrat
15
@Walfrat Lokalne zmienne są inicjalizowane wiele razy? Ojej Nikt nie sugerował ponownego użycia mieszkańców :)
Luaan
32
Członkowie @Walfrat wywołują skutki uboczne i powinny być stosowane tylko wtedy, gdy chcesz zachować stan między połączeniami z członkami publicznymi. To pytanie wydaje się dotyczyć użycia lokalnego do tymczasowego przechowywania obliczeń pośrednich.
Gusdor
4
P: Czy powinniśmy wyeliminować zmienne lokalne, jeśli możemy? Odp .: Nie.
simon
94

Niektóre bardzo entuzjastyczne komentarze stwierdzały to, ale żadna z odpowiedzi, które widziałem, nie dały, więc dodam to jako odpowiedź. Twoim głównym czynnikiem decydującym o tym problemie jest:

Debugowalność

Często programiści poświęcają dużo więcej czasu i wysiłku na debugowanie niż pisanie kodu.

Za pomocą zmiennych lokalnych możesz:

  • Punkt przerwania na linii, do której jest przypisany (wraz ze wszystkimi innymi dekoracjami punktów przerwania, takimi jak warunkowe punkty przerwania itp.)

  • Sprawdź / obejrzyj / wydrukuj / zmień wartość zmiennej lokalnej

  • Problemy z chwytaniem, które powstają w wyniku rzutów.

  • Czytelne ślady stosu (linia XYZ ma jedną operację zamiast 10)

Bez zmiennych lokalnych wszystkie powyższe zadania są albo znacznie trudniejsze, ekstremalnie trudne, albo wręcz niemożliwe, w zależności od twojego debuggera.

W związku z tym postępuj zgodnie z niesławną maksymą (napisz kod w taki sposób, jakby następnym deweloperem, który utrzyma go po sobie, jest szalony szalony, który wie, gdzie mieszkasz) i błądzić po stronie łatwiejszej debugowania , co oznacza użycie zmiennych lokalnych .

DVK
źródło
20
Dodam, że stosy śledzenia są znacznie mniej przydatne, gdy na jednej linii jest wiele wywołań. Jeśli masz wyjątek zerowy wskaźnik na linii 50 i jest 10 wywołań na tej linii, nie zawęża to zbyt wiele. W aplikacji produkcyjnej często będzie to większość informacji uzyskanych z raportu o usterce.
JimmyJames
3
Przejście przez kod jest również znacznie trudniejsze. Jeśli jest call () -> call () -> call () -> call (), ciężko jest przejść do trzeciego wywołania metody. Znacznie łatwiej, jeśli istnieją cztery zmienne lokalne
gnasher729
3
@FrankPuffer - mamy do czynienia ze światem rzeczywistym. Tam, gdzie debuggery nie robią tego, co proponujesz.
DVK
2
@DVK: W rzeczywistości jest więcej debuggerów, niż myślałem, co pozwala na sprawdzenie lub obejrzenie dowolnego wyrażenia. MS Visual Studio (od wersji 2013) ma tę funkcję dla C ++ i C #. Eclipse ma to dla Java. W innym komentarzu Faerindel wspomniał o IE11 dla JS.
Frank Puffer
4
@DVK: Tak, wiele debuggerów w świecie rzeczywistym nie robi tego, co proponuję. Ale to nie ma nic wspólnego z pierwszą częścią mojego komentarza. Po prostu uważam za szalone, że osoba, która będzie utrzymywać mój kod, będzie głównie z nim współdziałać za pomocą debuggera. Debugery są świetne do niektórych celów, ale jeśli otrzymają główne narzędzie do analizy kodu, powiedziałbym, że coś jest naprawdę nie tak i spróbuję to naprawić, zamiast uczynić kod bardziej debugowalnym.
Frank Puffer
47

Tylko jeśli ułatwi to zrozumienie kodu. W twoich przykładach myślę, że utrudnia to czytanie.

Eliminacja zmiennych w skompilowanym kodzie jest banalną operacją dla każdego szanowanego kompilatora. Możesz sprawdzić dane wyjściowe, aby je zweryfikować.

Jaka jest nazwa?
źródło
1
Ma to tyle samo wspólnego ze stylizacją, co używanie zmiennych. Pytanie zostało edytowane.
Jodrell
29

Twoje pytanie „czy dobrą praktyką jest wyeliminowanie zmiennej lokalnej, jeśli jest ona używana tylko raz w zakresie?” testuje złe kryteria. Narzędzie zmiennej lokalnej nie zależy od tego, ile razy jest używana, ale czy sprawia, że ​​kod jest wyraźniejszy. Oznaczanie wartości pośrednich znaczącymi nazwami może poprawić przejrzystość w sytuacjach takich jak ta, którą prezentujesz, ponieważ dzieli kod na mniejsze, łatwiejsze do strawienia fragmenty.

Moim zdaniem niezmodyfikowany kod, który przedstawiłeś, jest wyraźniejszy niż zmodyfikowany kod i dlatego powinien pozostać niezmieniony. W rzeczywistości rozważę wyciągnięcie zmiennej lokalnej ze zmodyfikowanego kodu w celu poprawy przejrzystości.

Nie spodziewałbym się żadnego wpływu wydajności na zmienne lokalne, a nawet jeśli taka istnieje, prawdopodobnie będzie ona zbyt mała, aby była warta rozważenia, chyba że kod jest w bardzo ścisłej pętli w krytycznej dla programu części programu.

Jack Aidley
źródło
Myślę, że ma to wiele wspólnego ze stylizacją i używaniem białych znaków jako czegokolwiek. Pytanie zostało zredagowane.
Jodrell
15

Może.

Osobiście wahałbym się przed wyeliminowaniem zmiennych lokalnych, jeśli w grę wchodzi typecast. Uważam, że twoja wersja kompaktowa jest mniej czytelna, ponieważ liczba nawiasów zaczyna zbliżać się do mojego mentalnego limitu. Wprowadza nawet nowy zestaw nawiasów, który nie był potrzebny w nieco bardziej szczegółowej wersji przy użyciu zmiennej lokalnej.

Podświetlanie składni dostarczone przez edytor kodu może w pewnym stopniu złagodzić takie obawy.

Moim zdaniem jest to lepszy kompromis:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc").acquire();

W ten sposób utrzymujemy jedną zmienną lokalną, a porzucamy drugą. W języku C # użyłbym słowa kluczowego var w pierwszym wierszu, ponieważ typecast już zawiera informacje o typie.

9Rune5
źródło
13

Chociaż akceptuję trafność odpowiedzi faworyzujących zmienne lokalne, zamierzam grać w adwokata diabła i przedstawiać pogardliwe poglądy.

Osobiście jestem nieco przeciwny używaniu zmiennych lokalnych wyłącznie do celów dokumentacji, chociaż sam ten powód wskazuje, że praktyka ma wartość: zmienne lokalne skutecznie pseudo dokumentują kod.

W twoim przypadku głównym problemem jest wcięcie, a nie brak zmiennych. Możesz (i powinieneś) sformatować kod w następujący sposób:

((PowerManager)getSystemService(POWER_SERVICE))
        .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag")
        .acquire();

Porównaj to z wersją ze zmiennymi lokalnymi:

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

W przypadku zmiennych lokalnych: nazwy dodają pewne informacje do czytnika, a typy są jasno określone, ale rzeczywiste wywołania metod są mniej widoczne i (moim zdaniem) nie są one czytane tak wyraźnie.

Bez użycia zmiennych lokalnych: jest bardziej zwięzły, a wywołania metod są bardziej widoczne, ale możesz poprosić o więcej informacji o tym, co jest zwracane. Niektóre IDE mogą ci to jednak pokazać.

Trzy dalsze komentarze:

  1. Moim zdaniem dodanie kodu, który nie ma zastosowania funkcjonalnego, może być czasem mylące, ponieważ czytelnik musi zdać sobie sprawę, że tak naprawdę nie ma zastosowania funkcjonalnego i jest po prostu „dokumentacją”. Na przykład, jeśli ta metoda miała długość 100 wierszy, nie byłoby oczywiste, że zmienne lokalne (a zatem wynik wywołania metody) nie były wymagane w późniejszym czasie, chyba że przeczytasz całą metodę.

  2. Dodanie zmiennych lokalnych oznacza, że ​​musisz określić ich typy, a to wprowadza zależność w kodzie, której inaczej by nie było. Jeśli typ zwracanych metod zmienił się trywialnie (np. Zmieniono jego nazwę), musiałbyś zaktualizować kod, a bez zmiennych lokalnych nie.

  3. Debugowanie może być łatwiejsze ze zmiennymi lokalnymi, jeśli twój debugger nie wyświetla wartości zwróconych z metod. Ale rozwiązaniem tego jest naprawienie braków w debuggerach, a nie zmiana kodu.

rghome
źródło
Odnośnie twoich dalszych komentarzy: 1. Nie powinno to stanowić problemu, jeśli postępujesz zgodnie z konwencją, że zmienne lokalne są zadeklarowane tak blisko, jak to możliwe, do miejsca, w którym są używane i zachowujesz rozsądną długość metod. 2. Nie w języku programowania z wnioskowaniem typu. 3. Dobrze napisany kod często wcale nie potrzebuje debuggera.
Robert Harvey
2
Twój pierwszy przykład kodu działa tylko wtedy, gdy tworzysz obiekty przy użyciu płynnych interfejsów lub wzorców „konstruktorów”, których nie używałbym wszędzie w kodzie, ale tylko selektywnie.
Robert Harvey
1
Myślę, że pytanie zakłada, że ​​zmienne lokalne są funkcjonalnie redundantne, a zatem ten typ interfejsu jest wymagany w przypadku. Prawdopodobnie moglibyśmy usunąć miejscowych, gdyby było inaczej przez różne wygięcia, ale miałem na myśli, że patrzymy na prosty przypadek.
rghome
1
Dla mnie wariant jest jeszcze gorszy pod względem czytelności. Mogłem być zbyt narażony na VB.net, ale mój pierwszy raz, gdy zobaczyłem twoją próbkę, brzmiał: „Gdzie poszło wyrażenie WITH? Czy przypadkowo go usunąłem?” Muszę dwa razy spojrzeć na to, co się dzieje, i to nie jest dobre, kiedy muszę ponownie odwiedzić kod wiele miesięcy później.
Tonny
1
@ Tonny dla mnie jest bardziej czytelny: miejscowi nie dodają niczego, co nie jest oczywiste z kontekstu. Mam włączoną Javę, więc nie ma żadnych withinstrukcji. Byłoby to normalne konwencje formatowania w Javie.
rghome
6

Jest tu mnóstwo motywacji. Wprowadzenie zmiennych tymczasowych może ułatwić odczyt kodu. Mogą jednak również zapobiegać innym utrudnieniom, a także utrudniać ich dostrzeżenie, na przykład Metodę Ekstrakcji i Zastąpić Temp Zapytaniem . Te ostatnie typy refaktoryzacji, w stosownych przypadkach, często zapewniają znacznie większe korzyści niż zmienna temp.

O motywacjach tych ostatnich refaktoryzacji Fowler pisze :

„Problem z tymczasami polega na tym, że są tymczasowe i lokalne. Ponieważ można je zobaczyć tylko w kontekście metody, w której są używane, tymczasowo zachęcają do stosowania dłuższych metod, ponieważ jest to jedyny sposób na osiągnięcie temperatury. zastępując temp metodą kwerendy, każda metoda w klasie może uzyskać informacje. To bardzo pomaga wymyślić czystszy kod dla klasy. ”

Tak, więc używaj temps, jeśli zwiększają czytelność kodu, zwłaszcza jeśli jest to lokalna norma dla ciebie i twojego zespołu. Należy jednak pamiętać, że może to czasem utrudnić wykrycie większych alternatywnych ulepszeń. Jeśli potrafisz rozwinąć umiejętność wyczuwania, kiedy warto przejść bez takich tymczasowych warunków, i poczuć się bardziej komfortowo, to prawdopodobnie dobra rzecz.

FWIW Osobiście unikałem czytania książki Fowlera „Refaktoryzacja” przez dziesięć lat, ponieważ wyobrażałem sobie, że nie ma wiele do powiedzenia na tak stosunkowo proste tematy. Całkowicie się myliłem. Kiedy w końcu to przeczytałem, zmieniło to moje codzienne praktyki kodowania, znacznie na lepsze.

Jonathan Hartley
źródło
1
Świetna odpowiedź. Najlepszy kod, który napisałem (i widziałem od innych), często zawierał wiele małych (2, 3 linie) metod, które mogłyby zastąpić takie zmienne tymczasowe. Powiązane jest to kontrowersyjne podejście, że prywatne metody śmierdzą ... Dobrze przemyślane i często stwierdziłem, że to prawda.
Stijn de Witt
Temps w tym pytaniu już odnosi się do funkcji, więc ta odpowiedź nie ma znaczenia dla pytania PO.
user949300,
@ user949300 Nie sądzę, żeby to było bez znaczenia. Tak, tempa w konkretnym przykładzie PO są wartościami zwracanymi z funkcji lub metod. Ale PO jest bardzo jasne, że to tylko przykładowy scenariusz. Rzeczywiste pytanie brzmi: „czy powinniśmy wyeliminować zmienne tymczasowe, kiedy możemy?”
Jonathan Hartley
1
OK, jestem sprzedany, ** próbowałem ** zmienić mój głos. Ale często, kiedy wychodzę poza ograniczony zakres pytania PO, jestem zdenerwowany. TAK może być kapryśny ... :-). Eee, nie mogę zmienić mojego głosu, dopóki nie edytujesz, cokolwiek ...
user949300
@ user949300 Święta krowa! Osoba, która zmienia zdanie po rozważnym rozważeniu problemów! Pan jest rzadkością, a ja zdejmuję wam czapkę.
Jonathan Hartley,
3

Dobrze jest wyeliminować zmienne lokalne, jeśli można w ten sposób zwiększyć czytelność kodu. Tego długiego linijki nie da się odczytać, ale potem ma dość pełny styl OO. Jeśli możesz to zredukować do czegoś takiego

acquireWakeLock(POWER_SERVICE, PARTIAL, "abc");

wtedy powiedziałbym, że to prawdopodobnie dobry pomysł. Być może możesz wprowadzić metody pomocnicze, aby sprowadzić to do czegoś podobnego; jeśli taki kod pojawia się wiele razy, być może warto.

po lewej stronie
źródło
2

Rozważmy tutaj Prawo Demetera . W artykule Wikipedii na temat LoD podano:

Prawo Demetera dla funkcji wymaga, aby metoda m obiektu O mogła wywoływać jedynie metody następujących rodzajów obiektów: [2]

  1. O sama
  2. parametry m
  3. Wszelkie obiekty utworzone / utworzone w obrębie m
  4. Bezpośrednie obiekty składowe O.
  5. Zmienna globalna dostępna dla O w zakresie m

Jedną z konsekwencji przestrzegania tego Prawa jest to, że należy unikać wywoływania metod obiektów zwracanych przez inne metody w długim kropkowanym ciągu, jak pokazano w drugim przykładzie powyżej:

((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag").acquire();

Naprawdę trudno jest zrozumieć, co to robi. Aby to zrozumieć, musisz zrozumieć, co robi każda procedura, a następnie rozszyfrować, która funkcja jest wywoływana na którym obiekcie. Pierwszy blok kodu

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock=powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

wyraźnie pokazuje, co próbujesz zrobić - otrzymujesz obiekt PowerManager, uzyskujesz obiekt WakeLock z PowerManager, a następnie nabywasz wakeLock. Powyższa zasada jest zgodna z zasadą nr 3 LoD - tworzysz te obiekty w swoim kodzie, a tym samym możesz ich użyć do zrobienia tego, czego potrzebujesz.

Być może innym sposobem myślenia jest pamiętanie, że podczas tworzenia oprogramowania należy pisać dla jasności, a nie dla zwięzłości. 90% całego rozwoju oprogramowania stanowi konserwacja. Nigdy nie pisz kodu, który nie byłby szczęśliwy w utrzymaniu.

Powodzenia.

Bob Jarvis
źródło
3
Byłbym zainteresowany tym, jak płynna składnia pasuje do tej odpowiedzi. Przy prawidłowym formatowaniu płynna składnia jest bardzo czytelna.
Gusdor
12
Nie to znaczy „Prawo Demetera”. Prawo Demetera nie jest ćwiczeniem polegającym na liczeniu kropek; w gruncie rzeczy oznacza to „rozmawiaj tylko z najbliższymi przyjaciółmi”.
Robert Harvey
5
Dostęp do tych samych metod i członków za pośrednictwem zmiennej pośredniej jest nadal równie poważnym naruszeniem prawa Demeter. Właśnie go zasłoniłeś, a nie naprawiłeś.
Jonathan Hartley,
2
Pod względem „Prawa Demetera” obie wersje są równie „złe”.
Hulk
@Gusdor zgodził się, to bardziej komentarz na temat stylu w przykładzie pytania. Pytanie zostało edytowane.
Jodrell
2

Należy zauważyć, że kod jest często odczytywany, z różnymi „głębokościami”. Ten kod:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

jest łatwy do „przeglądania”. To 3 wypowiedzi. Najpierw wymyślimy PowerManager. Następnie wymyślamy WakeLock. Potem . Widzę to naprawdę łatwo, patrząc na początek każdej linii; proste przypisania zmiennych są naprawdę łatwe do rozpoznania częściowo jako „Type varName = ...” i po prostu przeglądają w myślach „…”. Podobnie ostatnie stwierdzenie oczywiście nie jest formą przypisania, ale obejmuje tylko dwie nazwy, więc „główna istota” jest natychmiast widoczna. Często to wszystko, co musiałbym wiedzieć, gdybym tylko próbował odpowiedzieć „co robi ten kod?” na wysokim poziomie.acquirewakeLock

Jeśli gonię za subtelnym błędem, który, jak sądzę, jest tutaj, to oczywiście będę musiał omówić to bardziej szczegółowo i faktycznie zapamiętam „...”. Ale oddzielna struktura instrukcji wciąż pomaga mi wykonywać tę jedną instrukcję na raz (szczególnie przydatna, jeśli muszę wskoczyć głębiej w implementację rzeczy wywoływanych w każdej instrukcji; kiedy wracam, w pełni rozumiem „jedną jednostkę” i może następnie przejść do następnej instrukcji).

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

Teraz to wszystko jedno zdanie. Struktura najwyższego poziomu nie jest tak łatwa do odczytania; w oryginalnej wersji OP, bez łamania linii i wcięć w celu wizualnego komunikowania dowolnej struktury, musiałbym policzyć nawiasy, aby zdekodować ją w sekwencji 3 kroków. Jeśli niektóre wyrażenia wieloczęściowe są zagnieżdżone w sobie, a nie ułożone jako łańcuch wywołań metod, może wyglądać podobnie do tego, więc muszę być ostrożny, ufając temu bez liczenia nawiasów. A jeśli ufam wgłębieniu i przejdę do ostatniej rzeczy jako przypuszczalnego punktu tego wszystkiego, co .acquire()powie mi to samo?

Czasami jednak może to być to, czego chcesz. Jeśli zastosuję twoją transformację w połowie i napisam:

WakeLock wakeLock =
     ((PowerManeger)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLockTage");
wakeLock.acquire();

Teraz komunikuje się to z szybkim przeglądaniem „weź WakeLock, a potem acquireto”. Jeszcze prostszy niż pierwsza wersja. Od razu jest oczywiste, że rzecz, która została nabyta, to WakeLock. Jeśli uzyskanie PowerManagerjest tylko szczegółem szczegółowym, który jest nieistotny z punktu widzenia tego kodu, ale wakeLockjest to ważne, to może naprawdę pomóc w zakopaniu PowerManagerrzeczy, więc naturalne jest, aby przejrzeć je, jeśli po prostu próbujesz szybko uzyskać wyobrażenie o tym, co robi ten kod. A bez nazywania oznacza, że jest on używany tylko raz, a czasem to co jest ważne (jeśli reszta zakresu jest bardzo długa, musiałbym przeczytać wszystko, aby powiedzieć, czy kiedykolwiek zostanie ponownie użyty; chociaż użycie jawnych podzakresów może być innym sposobem rozwiązania tego, jeśli twój język je obsługuje).

Co powoduje, że wszystko zależy od kontekstu i tego, co chcesz komunikować. Podobnie jak w przypadku pisania prozy w języku naturalnym, zawsze istnieje wiele sposobów na napisanie danego fragmentu kodu, który jest zasadniczo równoważny pod względem zawartości informacyjnej. Podobnie jak w przypadku pisania prozy w języku naturalnym, jak wybrać pomiędzy nimi powinna generalnie nie będzie stosować zasady mechanistyczne jak „wyeliminować lokalną zmienną, która występuje tylko raz”. Raczej jakzdecydujesz się zapisać kod będzie podkreślał pewne rzeczy, a odznaczał inne. Powinieneś starać się dokonywać tych wyborów świadomie (w tym czasami pisać mniej czytelny kod z przyczyn technicznych), w oparciu o to, co naprawdę chcesz podkreślić. Pomyśl szczególnie o tym, co posłuży czytelnikom, którzy po prostu potrzebują „poznać istotę” twojego kodu (na różnych poziomach), ponieważ będzie to miało miejsce znacznie częściej niż bardzo ścisłe czytanie wyrażenie po wyrażeniu.

Ben
źródło
Dla mnie, chociaż nie mam wiedzy na temat API, jest bardzo oczywiste, co robi kod, nawet bez zmiennych. getSystemService(POWER_SERVICE)dostaje menedżera mocy. .newWakeLockdostaje blokadę czuwania. .acquirenabywa to. OK, nie znam wszystkich typów, ale mogę to znaleźć w IDE, jeśli zajdzie taka potrzeba.
rghome
Może być dla ciebie oczywiste, co powinien zrobić kod , ale nie masz pojęcia, co tak naprawdę robi. Jeśli szukam błędu, wiem tylko, że jakiś kod gdzieś nie robi tego, co powinien, i muszę znaleźć, który kod.
gnasher729
@ gnasher729 Tak. Polowanie na robaki to przykład, który podałem, kiedy trzeba uważnie przeczytać wszystkie szczegóły. Jedną z rzeczy, która naprawdę pomaga znaleźć kod, który nie robi tego, co powinien, jest możliwość łatwego zobaczenia, jakie były intencje autora.
Ben
kiedy wracam, w pełni zrozumiałem „jedną jednostkę” i mogę przejść do następnej wypowiedzi. Właściwie nie. W rzeczywistości zmienne lokalne uniemożliwiają pełne zrozumienie. Ponieważ wciąż trwają ... zajmują przestrzeń mentalną ... co się z nimi stanie w następnych 20 wypowiedziach ... bęben ... Bez miejscowych byłbyś pewien, że obiekty zniknęły i nie można nic zrobić z nimi w następnych wierszach. Nie musisz o nich pamiętać. Z returntego samego powodu lubię korzystać z metod tak szybko, jak to możliwe.
Stijn de Witt
2

Jest to nawet antypattern z własną nazwą: Train Wreck . Podano już kilka powodów, dla których należy unikać wymiany:

  • Trudniej czytać
  • Trudniejsze do debugowania (zarówno do oglądania zmiennych, jak i wykrywania lokalizacji wyjątków)
  • Naruszenie prawa Demeter (LoD)

Zastanów się, czy ta metoda nie zna zbyt wiele z innych obiektów. Łączenie metod jest alternatywą, która może pomóc w zmniejszeniu sprzężenia.

Pamiętaj również, że odniesienia do obiektów są naprawdę tanie.

Borjab
źródło
Ehm, czy poprawiony kod nie łączy łańcuchów metod? EDYCJA przeczytaj link do artykułu, więc teraz widzę różnicę. Całkiem dobry artykuł dzięki!
Stijn de Witt
Dzięki za sprawdzenie. W każdym razie łańcuch metod może działać w niektórych przypadkach, a samo pozostawienie zmiennej może być właściwą decyzją. Zawsze dobrze jest wiedzieć, dlaczego ludzie nie zgadzają się z twoją odpowiedzią.
Borjab
Wydaje mi się, że przeciwieństwem „wraku pociągu” jest „linia lokalna”. Jest to pociąg między dwoma głównymi miastami, który zatrzymuje się w każdej wiejskiej wiosce na wypadek, gdyby ktoś chciał wsiąść lub wysiąść, nawet jeśli w większość dni nikt tego nie robi.
rghome
1

Zadane pytanie brzmi: „Czy powinniśmy wyeliminować zmienne lokalne, jeśli możemy”?

Nie, nie powinieneś eliminować tylko dlatego, że możesz.

Powinieneś postępować zgodnie ze wskazówkami dotyczącymi kodowania w swoim sklepie.

W większości zmienne lokalne ułatwiają czytanie i debugowanie kodu.

Podoba mi się to, co zrobiłeś PowerManager powerManager. Dla mnie mała litera tej klasy oznacza, że ​​jest to jednorazowe użycie.

Kiedy nie powinieneś, to czy zmienna będzie utrzymywać drogie zasoby. Wiele języków ma składnię zmiennej lokalnej, którą należy wyczyścić / zwolnić. W języku C # używa.

using(SQLconnection conn = new SQLconnnection())
{
    using(SQLcommand cmd = SQLconnnection.CreateCommand())
    {
    }
}
paparazzo
źródło
Nie jest to tak naprawdę związane ze zmiennymi lokalnymi, ale z czyszczeniem zasobów ... Nie znam się dobrze na C #, ale byłbym zaskoczony, gdyby using(new SQLConnection()) { /* ... */ }nie był również zgodny z prawem (czy to byłoby przydatne, to inna sprawa :).
Stijn de Witt
1

W innych odpowiedziach nie wspomniano o jednym ważnym aspekcie: za każdym razem, gdy dodajesz zmienną, wprowadzasz stan zmienny. Jest to zwykle zła rzecz, ponieważ sprawia, że ​​kod jest bardziej złożony, a zatem trudniejszy do zrozumienia i przetestowania. Oczywiście im mniejszy zakres zmiennej, tym mniejszy problem.

Tym, czego chcesz, nie jest zmienna, której wartość można modyfikować, ale stała tymczasowa. Dlatego rozważ wyrażenie tego w kodzie, jeśli Twój język na to pozwala. W Javie możesz używać, finalaw C ++ możesz używać const. W większości języków funkcjonalnych jest to zachowanie domyślne.

Prawdą jest, że zmienne lokalne są najmniej szkodliwym typem stanu zmiennego. Zmienne składowe mogą powodować znacznie więcej problemów, a zmienne statyczne są jeszcze gorsze. Nadal uważam, że ważne jest, aby wyrażać w kodzie jak najdokładniej, co ma robić. Istnieje ogromna różnica między zmienną, którą można zmodyfikować później, a samą nazwą dla wyniku pośredniego. Więc jeśli twój język pozwala wyrazić tę różnicę, po prostu zrób to.

Frank Puffer
źródło
2
Nie głosowałem za twoją odpowiedzią, ale zgaduję, że jest to związane z faktem, że zmienne lokalne nie są ogólnie uważane za „stan” w imperatywnych językach, chyba że mówisz o jakiejś długotrwałej metodzie. Zgadzam się na użycie parametru final / const jako najlepszej praktyki, ale jest mało prawdopodobne, aby wprowadzenie zmiennej w celu po prostu przerwania połączenia łańcuchowego doprowadziło do problemów spowodowanych stanem zmiennym. W rzeczywistości użycie zmiennych lokalnych pomaga radzić sobie ze stanem zmiennym. Jeśli metoda może zwracać różne wartości w różnych momentach, w przeciwnym razie mogą pojawić się naprawdę interesujące błędy.
JimmyJames
@JimmyJames: Dodałem wyjaśnienie do mojej odpowiedzi. Ale jest jedna część twojego komentarza, której nie rozumiałem: „użycie zmiennych lokalnych pomaga radzić sobie ze stanem zmiennym”. Czy możesz to wyjaśnić?
Frank Puffer,
1
Załóżmy na przykład, że muszę sprawdzić wartość, a jeśli jest ona wyższa niż 10, uruchom alert. Załóżmy, że ta wartość pochodzi z metody getFoo(). Jeśli if(getFoo() > 10) alert(getFoo());uniknę deklaracji lokalnej, skończę, ale getFoo () może zwrócić różne wartości dla dwóch różnych wywołań. Mógłbym wysłać ostrzeżenie o wartości mniejszej lub równej 10, co jest co najwyżej mylące i wróci do mnie jako wada. Tego rodzaju rzeczy stają się ważniejsze w przypadku współbieżności. Lokalne zadanie ma charakter atomowy.
JimmyJames
Bardzo dobra uwaga. Może powodem nie lubię tych mieszkańców .... Czy masz zamiar zrobić coś z tym przykład po wywołaniu tej metody? Pozwól, że sprawdzę resztę metody .... mmm ok nie. I to , co masz ... co robisz z tym (jeszcze raz skanuję resztę metody) ... mmm znowu nic ... ok, więc dlaczego oni tam są? O tak, powinno być bardziej czytelne ... ale bez nich jestem pewien , że już ich nie używasz, więc nie muszę czytać reszty metody. PowerManagerWakeLock
Stijn de Witt
Podczas ostatniego przemówienia mówca argumentował, że najlepszym sposobem wykazania, że ​​zmienna jest ostateczna, jest napisanie krótkich metod, w których oczywiste jest, że zmienna się nie zmienia . Jeśli finaljest to potrzebne dla zmiennej lokalnej w metodzie, twoja metoda jest za długa.
user949300,