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 powerManager
i wakeLock
mogą 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?
Odpowiedzi:
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
powerManager
w rejestrze 1 , niezależnie od tego, czy używana jest zmienna lokalna, czy nie, wywołanienewWakeLock
metody. To samo dotyczywakeLock
. 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ół.
źródło
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 .
źródło
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ć.
źródło
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.
źródło
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:
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.
źródło
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:
Porównaj to z wersją ze zmiennymi lokalnymi:
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:
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ę.
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.
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.
źródło
with
instrukcji. Byłoby to normalne konwencje formatowania w Javie.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 :
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.
źródło
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
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.
źródło
Rozważmy tutaj Prawo Demetera . W artykule Wikipedii na temat LoD podano:
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:
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
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.
źródło
Należy zauważyć, że kod jest często odczytywany, z różnymi „głębokościami”. Ten kod:
jest łatwy do „przeglądania”. To 3 wypowiedzi. Najpierw wymyślimy
PowerManager
. Następnie wymyślamyWakeLock
. 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.acquire
wakeLock
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).
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:
Teraz komunikuje się to z szybkim przeglądaniem „weź
WakeLock
, a potemacquire
to”. Jeszcze prostszy niż pierwsza wersja. Od razu jest oczywiste, że rzecz, która została nabyta, toWakeLock
. Jeśli uzyskaniePowerManager
jest tylko szczegółem szczegółowym, który jest nieistotny z punktu widzenia tego kodu, alewakeLock
jest to ważne, to może naprawdę pomóc w zakopaniuPowerManager
rzeczy, 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.
źródło
getSystemService(POWER_SERVICE)
dostaje menedżera mocy..newWakeLock
dostaje blokadę czuwania..acquire
nabywa to. OK, nie znam wszystkich typów, ale mogę to znaleźć w IDE, jeśli zajdzie taka potrzeba.return
tego samego powodu lubię korzystać z metod tak szybko, jak to możliwe.Jest to nawet antypattern z własną nazwą: Train Wreck . Podano już kilka powodów, dla których należy unikać wymiany:
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.
źródło
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.
źródło
using(new SQLConnection()) { /* ... */ }
nie był również zgodny z prawem (czy to byłoby przydatne, to inna sprawa :).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ć,
final
aw 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.
źródło
getFoo()
. Jeśliif(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.PowerManager
WakeLock
final
jest to potrzebne dla zmiennej lokalnej w metodzie, twoja metoda jest za długa.