Czy sensowne jest tworzenie bloków tylko w celu zmniejszenia zakresu zmiennej?

38

Piszę program w Javie, w którym w pewnym momencie muszę załadować hasło do mojego magazynu kluczy. Dla zabawy starałem się, aby moje hasło w Javie było jak najkrótsze, wykonując następujące czynności:

//Some code
....

KeyManagerFactory keyManager = KeyManagerFactory.getInstance("SunX509");
Keystore keyStore = KeyStore.getInstance("JKS");

{
    char[] password = getPassword();
    keyStore.load(new FileInputStream(keyStoreLocation), password);
    keyManager.init(keyStore, password);
}

...
//Some more code

Teraz wiem, że w tym przypadku to trochę głupie. Jest kilka innych rzeczy, które mogłem zrobić, większość z nich faktycznie lepiej (nie mogłem w ogóle użyć zmiennej).

Byłem jednak ciekawy, czy istnieje przypadek, w którym robienie tego nie było tak głupie. Jedyną rzeczą, o której mogę pomyśleć, jest to, czy chcesz ponownie użyć wspólnych nazw zmiennych, takich jak count, lub temp, ale dobre konwencje nazewnictwa i krótkie metody sprawiają, że jest to mało prawdopodobne.

Czy istnieje przypadek, w którym zastosowanie bloków tylko w celu zmniejszenia zakresu zmiennej ma sens?

Lord Farquaad
źródło
13
@gnat .... co? Nie wiem nawet, jak edytować moje pytanie, aby było mniej podobne do tego docelowego duplikatu. Jaka część tego doprowadziła cię do przekonania, że ​​te pytania są takie same?
Lord Farquaad
22
Zwykle odpowiedzią na to jest to, że anonimowy blok powinien być metodą o znaczącej nazwie, co daje automatyczną dokumentację i pożądany ograniczony zakres. Nie mogę wymyślić sytuacji, w której wyodrębnienie metody nie jest lepsze niż wstawienie nagiego bloku.
Kilian Foth
4
Jest kilka podobnych pytań, które zadają to pytanie w C ++: Czy tworzenie bloków kodu jest złą praktyką? i kod struktury z nawiasami klamrowymi . Być może jedna z nich zawiera odpowiedź na to pytanie, chociaż C ++ i Java różnią się pod tym względem.
amon
2
Możliwy duplikat Czy tworzenie bloków kodu jest złą praktyką?
Panzercrisis
4
@gnat Dlaczego to pytanie jest w ogóle otwarte? To jedna z najszerszych rzeczy, jakie kiedykolwiek widziałem. Czy to próba pytania kanonicznego?
jpmc26,

Odpowiedzi:

34

Po pierwsze, rozmawiając z podstawową mechaniką:

W zakresie C ++ == dożywotnie b / c destruktory są wywoływane przy wyjściu z zakresu. Ponadto, ważne rozróżnienie w C / C ++ możemy deklarować obiekty lokalne. W środowisku wykonawczym dla C / C ++ kompilator ogólnie przydziela ramkę stosu dla metody, która jest tak duża, jak może być potrzebna, z wyprzedzeniem, zamiast przydzielać więcej miejsca na stosie przy wejściu do każdego zakresu (który deklaruje zmienne). Kompilator zwija się lub spłaszcza zakresy.

Kompilator C / C ++ może ponownie wykorzystać przestrzeń pamięci stosu dla lokalizacji lokalnych, które nie powodują konfliktu w czasie użytkowania (zazwyczaj użyje analizy rzeczywistego kodu w celu ustalenia tego, a nie zakresu, b / c, który jest dokładniejszy niż zakres!).

Wspominam o składni Java C / C ++ b / c, tj. Nawiasach klamrowych i określaniu zakresu, przynajmniej częściowo pochodzi z tej rodziny. A także dlatego, że C ++ pojawił się w komentarzach.

Natomiast nie można mieć obiektów lokalnych w Javie: wszystkie obiekty są obiektami sterty, a wszystkie obiekty lokalne / formale są albo zmiennymi referencyjnymi, albo typami pierwotnymi (btw, to samo dotyczy statyki).

Co więcej, w Javie zakres i czas życia nie są dokładnie zrównane: bycie w / poza zakresem jest przeważnie koncepcją czasu kompilacji dotyczącą dostępności i konfliktów nazw; tak naprawdę nic się nie dzieje w Javie po wyjściu z zakresu w odniesieniu do czyszczenia zmiennych. Wyrzucanie elementów bezużytecznych Java określa (punkt końcowy) żywotność obiektów.

Również mechanizm kodu bajtowego Javy (dane wyjściowe kompilatora Java) ma tendencję do promowania zmiennych użytkownika zadeklarowanych w ograniczonych zakresach do najwyższego poziomu metody, ponieważ nie ma funkcji zasięgu na poziomie kodu bajtowego (jest to podobne do obsługi C / C ++ stos). W najlepszym wypadku kompilator może ponownie wykorzystywać lokalne gniazda zmiennych, ale (w przeciwieństwie do C / C ++) tylko wtedy, gdy ich typ jest taki sam.

(Jednak, aby mieć pewność, że podstawowy kompilator JIT w środowisku wykonawczym może ponownie użyć tego samego miejsca do przechowywania stosu dla dwóch lokali o różnych typach (i różnych szczelinach), z wystarczającą analizą kodu bajtowego.)

Mówiąc o przewadze programisty, chciałbym zgodzić się z innymi, że (prywatna) metoda jest lepszą konstrukcją, nawet jeśli zostanie użyta tylko raz.

Nadal jednak nie ma nic złego w używaniu go do dekonfliktu nazw.

Zrobiłem to w rzadkich okolicznościach, gdy tworzenie indywidualnych metod jest ciężarem. Na przykład pisząc interpreter za pomocą dużej switchinstrukcji w pętli, mogę (w zależności od czynników) wprowadzić osobny blok dla każdego casez nich, aby poszczególne przypadki były bardziej od siebie oddzielone, zamiast uczynić każdą sprawę inną metodą (każda który jest wywoływany tylko raz).

(Zauważ, że jako kod w blokach poszczególne przypadki mają dostęp do instrukcji „break;” i „kontynuuj;” dotyczących otaczającej pętli, podczas gdy metody wymagałyby zwracania boolanów i użycia warunkowych w celu uzyskania dostępu do tych kontroli sprawozdania.)

Erik Eidt
źródło
Bardzo interesująca odpowiedź (+1)! Jednak jedno uzupełnienie części C ++. Jeśli hasło jest ciągiem w bloku, obiekt ciągu przydzieli miejsce w wolnym magazynie na część o zmiennej wielkości. Po opuszczeniu bloku ciąg zostanie zniszczony, co spowoduje zwolnienie części zmiennej. Ale ponieważ nie ma go na stosie, nic nie gwarantuje, że zostanie wkrótce zastąpiony. Lepiej więc zarządzaj poufnością, nadpisując każdą z jej postaci przed zniszczeniem łańcucha ;-)
Christophe
1
@ErikEidt Próbowałem zwrócić uwagę na problem z mówieniem o „C / C ++”, tak jakby to było „coś”, ale tak nie jest: „W środowisku wykonawczym dla C / C ++ kompilator zazwyczaj przydziela ramkę stosu dla metody, która jest tak duża, jak może być potrzebna ", ale C w ogóle nie ma metod. Ma funkcje. Są różne, szczególnie w C ++.
tchrist
7
Natomiast nie można mieć obiektów lokalnych w Javie: wszystkie obiekty są obiektami sterty, a wszystkie obiekty lokalne / formale są albo zmiennymi referencyjnymi, albo typami pierwotnymi (btw, to samo dotyczy statyki). ” - Należy pamiętać, że jest to nie do końca prawda, szczególnie nie dla lokalnych zmiennych metod. Analiza ucieczki może przydzielić obiekty do stosu.
Boris the Spider
1
W najlepszym wypadku kompilator może ponownie wykorzystywać lokalne gniazda zmiennych, ale (w przeciwieństwie do C / C ++) tylko wtedy, gdy ich typ jest taki sam. ”Jest po prostu zły. Na poziomie kodu bajtowego zmienne lokalne można przypisywać i ponownie przypisywać dowolnie, jedynym ograniczeniem jest to, że kolejne użycie jest zgodne z typem ostatnio przypisanego elementu. Ponowne użycie miejscowych zmiennych na zmienne jest normą, np { int x = 42; String s="blah"; } { long y = 0; }. longZmienna ponownie użyje szczelin obu zmiennych xi swszystkich powszechnie używanych kompilatorów ( javaci ecj).
Holger
1
@Boris the Spider: to często powtarzane stwierdzenie potoczne, które tak naprawdę nie pasuje do tego, co się dzieje. Analiza ucieczki może włączyć Scalarization, czyli rozkład pól obiektu na zmienne, które następnie podlegają dalszej optymalizacji. Niektóre z nich mogą zostać wyeliminowane jako nieużywane, inne mogą zostać złożone za pomocą zmiennych źródłowych użytych do ich zainicjowania, inne zostaną zmapowane do rejestrów procesora. Wynik rzadko pasuje do tego, co ludzie mogą sobie wyobrazić, mówiąc „przydzielone na stosie”.
Holger
27

Moim zdaniem łatwiej byłoby wyciągnąć blok z własnej metody. Jeśli zostawiłeś ten blok, mam nadzieję, że zobaczysz komentarz wyjaśniający, dlaczego umieszczasz ten blok w pierwszej kolejności. W tym momencie po prostu czystsze jest posiadanie wywołania metody, w initializeKeyManager()którym zmienna hasła jest ograniczona do zakresu metody i pokazuje twoje zamiary za pomocą bloku kodu.

Casey Langford
źródło
2
Myślę, że twoja odpowiedź jest kluczowa dla przypadku, to jest pośredni krok podczas refaktoryzacji. Przydatne może być ograniczenie zakresu i sprawdzenie, że kod nadal działa tuż przed wypakowaniem metody.
RubberDuck,
16
@RubberDuck to prawda, ale w takim przypadku reszta z nas nigdy nie powinna tego zobaczyć, więc nie ma znaczenia, co myślimy. To, na co dorosły koder i zgodzący się kompilator wstają w zaciszu własnej kabiny, nie jest dla nikogo problemem.
candied_orange
@candied_orange Obawiam się, że nie rozumiem :(
Chcesz
@doubleOrt Miałem na myśli pośrednie etapy refaktoryzacji są prawdziwe i konieczne, ale na pewno nie muszą być objęte kontrolą źródła, gdzie każdy może na to spojrzeć. Po prostu zakończ refaktoryzację przed odprawą.
candied_orange
@candied_orange Och, myślałem, że się kłócisz i nie przedłużasz argumentu RubberDuck.
doubleOrt
17

Mogą być przydatne w Rust, z surowymi zasadami zaciągania pożyczek. I ogólnie w językach funkcjonalnych, w których ten blok zwraca wartość. Ale w językach takich jak Java? Chociaż pomysł wydaje się elegancki, w praktyce jest rzadko używany, więc zamieszanie związane z jego widzeniem zmiażdży potencjalną klarowność ograniczenia zakresu zmiennych.

Jest jednak jeden przypadek, w którym uważam to za przydatne - w switchoświadczeniu! Czasami chcesz zadeklarować zmienną w case:

switch (op) {
    case ADD:
        int result = a + b;
        System.out.printf("%s + %s = %s\n", a, b, result);
        break;
    case SUB:
        int result = a - b;
        System.out.printf("%s - %s = %s\n", a, b, result);
        break;
}

To jednak zawiedzie:

error: variable result is already defined in method main(String[])
            int result = a - b;

switch instrukcje są dziwne - są instrukcjami kontrolnymi, ale z powodu ich upadku nie wprowadzają zakresów.

Tak więc - możesz po prostu użyć bloków, aby samodzielnie utworzyć zakresy:

switch (op) {
    case ADD: {
        int result = a + b;
        System.out.printf("%s + %s = %s\n", a, b, result);
    } break;
    case SUB: {
        int result = a - b;
        System.out.printf("%s - %s = %s\n", a, b, result);
    } break;
}
Idan Arye
źródło
6
  • Ogólna sprawa:

Czy istnieje przypadek, w którym zastosowanie bloków tylko w celu zmniejszenia zakresu zmiennej ma sens?

Ogólnie dobrą praktyką jest krótkie metody. Zmniejszenie zakresu niektórych zmiennych może być znakiem, że twoja metoda jest dość długa, wystarczająco, aby sądzić, że zakres zmiennych musi zostać zmniejszony. W takim przypadku prawdopodobnie warto utworzyć osobną metodę dla tej części kodu.

Moim zdaniem problematyczne są przypadki, gdy ta część kodu wykorzystuje więcej niż garść argumentów. Są sytuacje, w których możesz skończyć z małymi metodami, które wymagają wielu parametrów (lub wymagają obejścia, aby zasymulować zwracanie wielu wartości). Rozwiązaniem tego konkretnego problemu jest utworzenie kolejnej klasy, która przechowuje różne zmienne, których używasz, i implementuje wszystkie kroki, o których myślisz w swoich stosunkowo krótkich metodach: jest to typowy przypadek użycia wzorca konstruktora .

To powiedziawszy, wszystkie te wytyczne kodowania mogą być czasami stosowane luźno. Czasami zdarzają się przypadki, w których kod może być bardziej czytelny, jeśli trzymasz go w nieco dłuższej metodzie, zamiast dzielić go na małe pod-metody (lub wzorce konstruktora). Zazwyczaj działa to w przypadku problemów, które są średniej wielkości, dość liniowe i gdzie zbyt duże rozdzielenie faktycznie utrudnia czytelność (szczególnie, jeśli trzeba przeskakiwać między zbyt wieloma metodami, aby zrozumieć, co robi kod).

Może to mieć sens, ale jest bardzo rzadkie.

  • Twój przypadek użycia:

Oto twój kod:

KeyManagerFactory keyManager = KeyManagerFactory.getInstance("SunX509");
Keystore keyStore = KeyStore.getInstance("JKS");

{
    char[] password = getPassword();
    keyStore.load(new FileInputStream(keyStoreLocation), password);
    keyManager.init(keyStore, password);
}

Tworzysz FileInputStream, ale nigdy go nie zamykasz.

Jednym ze sposobów rozwiązania tego jest użycie instrukcji try-with-resources . Obejmuje zakres InputStream, i możesz również użyć tego bloku, aby zmniejszyć zakres innych zmiennych, jeśli chcesz (w granicach rozsądku, ponieważ te linie są powiązane):

KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Keystore keyStore = KeyStore.getInstance("JKS");

try (InputStream fis = new FileInputStream(keyStoreLocation)) {
    char[] password = getPassword();
    keyStore.load(fis, password);
    keyManager.init(keyStore, password);
}

(Nawiasem mówiąc, często lepiej jest używać getDefaultAlgorithm()zamiast tego SunX509.)

Ponadto, mimo że porady dotyczące rozdzielania fragmentów kodu na osobne metody są ogólnie uzasadnione. Rzadko warto, jeśli dotyczy tylko 3 wierszy (jeśli tak, to przypadek, w którym utworzenie oddzielnej metody utrudnia czytelność).

Bruno
źródło
„Zmniejszenie zakresu niektórych zmiennych może być znakiem, że twoja metoda jest dość długa” - Może być znakiem, ale może również i IMHO powinno być użyte w celu udokumentowania, jak długo używana jest wartość zmiennej lokalnej. W rzeczywistości dla języków nieobsługujących zakresów zagnieżdżonych (np. Python) byłbym wdzięczny, gdyby odpowiedni komentarz kodu podał „koniec życia” zmiennej. Jeśli ta sama zmienna zostanie ponownie użyta później, łatwo będzie ustalić, czy musiała ona (lub powinna) zostać zainicjowana nową wartością gdzieś przed (ale po komentarzu „końca życia”).
Jonny Dee
2

Moim skromnym zdaniem, generalnie jest to coś, czego należy unikać głównie w przypadku kodu produkcyjnego, ponieważ generalnie nie kusi cię to, z wyjątkiem sporadycznych funkcji wykonujących różne zadania. Zwykle robię to w kodzie złomu używanym do testowania rzeczy, ale nie znajduję pokusy, aby to zrobić w kodzie produkcyjnym, w którym wcześniej zastanawiałem się, co powinna zrobić każda funkcja, ponieważ wtedy funkcja będzie naturalnie mieć bardzo ograniczony zakres w odniesieniu do jego stanu lokalnego.

Nigdy tak naprawdę nie widziałem przykładów użycia anonimowych bloków w ten sposób (nie obejmujących warunkowych, trybloku dla transakcji itp.) W celu znacznego ograniczenia zakresu w funkcji, która nie nasuwa pytania, dlaczego nie może podzielić dalej na prostsze funkcje o zmniejszonych zakresach, jeśli faktycznie skorzystało ono praktycznie z prawdziwego punktu widzenia SE z anonimowych bloków. Zwykle jest to eklektyczny kod, który robi wiele luźno powiązanych lub bardzo niezwiązanych ze sobą rzeczy, w których najbardziej kusi nas, aby po to sięgnąć.

Na przykład, jeśli próbujesz to zrobić, aby ponownie użyć zmiennej o nazwie count, sugeruje to, że liczysz dwie różne rzeczy. Jeśli nazwa zmiennej ma być tak krótka jak count, to sensowne jest dla mnie powiązanie jej z kontekstem funkcji, która potencjalnie może po prostu liczyć jeden rodzaj rzeczy. Następnie możesz natychmiast spojrzeć na nazwę funkcji i / lub dokumentację, zobaczyć counti od razu wiedzieć, co to znaczy w kontekście tego, co robi funkcja, bez analizowania całego kodu. Często nie znajduję dobrego argumentu dla funkcji liczącej dwie różne rzeczy, które ponownie wykorzystują tę samą nazwę zmiennej w sposób, który sprawia, że ​​anonimowe zakresy / bloki są tak atrakcyjne w porównaniu z alternatywami. To nie tak sugeruje, że wszystkie funkcje muszą liczyć tylko jedną rzecz. JA'korzyści inżynieryjne dla funkcji wykorzystującej tę samą nazwę zmiennej do zliczenia dwóch lub więcej rzeczy i używania anonimowych bloków w celu ograniczenia zakresu każdej pojedynczej liczby. Jeśli funkcja jest prosta i przejrzysta, to nie koniec świata, aby mieć dwie różnie nazwane zmienne zliczające, przy czym pierwsza może mieć kilka linii widoczności więcej, niż jest to wymagane. Takie funkcje zasadniczo nie są źródłem błędów pozbawionych takich anonimowych bloków, aby jeszcze bardziej ograniczyć minimalny zakres zmiennych lokalnych.

Nie jest to sugestia dla zbytecznych metod

Nie ma to sugerować, że wymusisz tworzenie metod w celu zmniejszenia zasięgu. Jest to zapewne tak samo złe lub złe, i to, co sugeruję, nie powinno wymagać bardziej niezręcznych prywatnych metod „pomocnika” niż anonimowych zakresów. To za dużo myśli o obecnym kodzie i jak zmniejszyć zakres zmiennych, niż o koncepcyjnym rozwiązaniu problemu na poziomie interfejsu w sposób zapewniający naturalnie czystą, krótką widoczność lokalnych stanów funkcji bez głębokiego zagnieżdżania bloków i ponad 6 poziomów wcięcia. Zgadzam się z Bruno, że możesz utrudnić czytelność kodu, mocno wciskając 3 wiersze kodu do funkcji, ale zaczyna się to od założenia, że ​​rozwijasz funkcje, które tworzysz w oparciu o istniejącą implementację, zamiast projektować funkcje bez angażowania się w implementacje. Jeśli robisz to w ten drugi sposób, nie ma potrzeby anonimowych bloków, które nie służą żadnemu celowi poza ograniczeniem zakresu zmiennej w ramach danej metody, chyba że próbujesz gorliwie zmniejszyć zakres zmiennej tylko o kilka mniej linii nieszkodliwego kodu gdzie egzotyczne wprowadzenie tych anonimowych bloków prawdopodobnie wnosi tyle samo intelektualnego obciążenia, ile usuwają.

Próbując jeszcze bardziej zmniejszyć minimalne zakresy

Jeśli warto było ograniczyć zakresy zmiennych lokalnych do absolutnego minimum, wówczas powinna istnieć szeroka akceptacja takiego kodu:

ImageIO.write(new Robot("borg").createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())), "png", new File(Db.getUserId(User.handle()).toString()));

... ponieważ powoduje to minimalną widoczność stanu, nawet nie tworząc zmiennych odnoszących się do nich w pierwszej kolejności. Nie chcę być tak dogmatyczny, ale tak naprawdę uważam, że pragmatycznym rozwiązaniem jest unikanie anonimowych bloków, gdy jest to możliwe, tak jak unikanie monstrualnej linii kodu powyżej i jeśli wydają się one absolutnie konieczne w kontekście produkcyjnym z perspektywa poprawności i utrzymywania niezmienników w obrębie funkcji, to zdecydowanie uważam, że sposób organizacji kodu w funkcje i projektowania interfejsów jest warty ponownego zbadania. Oczywiście, jeśli twoja metoda ma 400 linii długości i zasięg zmiennej jest widoczny dla 300 linii kodu więcej niż jest to potrzebne, może to być prawdziwy problem inżynieryjny, ale niekoniecznie jest to problem do rozwiązania za pomocą anonimowych bloków.

Jeśli nic więcej, korzystanie z anonimowych bloków w całym miejscu jest egzotyczne, a nie idiomatyczne, a egzotyczny kod niesie ryzyko, że zostanie znienawidzony przez innych, jeśli nie przez ciebie, lata później.

Praktyczna przydatność ograniczenia zakresu

Ostateczną użytecznością ograniczenia zakresu zmiennych jest umożliwienie poprawnego zarządzania stanem i utrzymania go w prawidłowy sposób oraz umożliwienia łatwego uzasadnienia tego, co robi dana część bazy kodu - w celu utrzymania niezmienników koncepcyjnych. Jeśli lokalne zarządzanie stanem jednej funkcji jest tak złożone, że musisz siłą zmniejszyć zakres za pomocą anonimowego bloku kodu, który nie ma być sfinalizowany i dobry, to znowu jest to dla mnie znak, że sama funkcja musi zostać ponownie zbadana . Jeśli masz trudności z rozumowaniem zarządzania stanem zmiennych w lokalnym zakresie funkcji, wyobraź sobie trudność z rozumowaniem zmiennych prywatnych dostępnych dla każdej metody całej klasy. Nie możemy używać anonimowych bloków, aby zmniejszyć ich widoczność. Dla mnie pomaga zacząć od akceptacji, że zmienne będą miały nieco szerszy zakres, niż powinny mieć w wielu językach, pod warunkiem, że nie wymknie się to z sytuacji, w której będziesz miał trudności z utrzymaniem niezmienników. To nie jest coś do rozwiązania z anonimowymi blokami, ponieważ uważam to za akceptowalne z pragmatycznego punktu widzenia.


źródło
Jako ostatnie zastrzeżenie, jak wspomniałem, nadal od czasu do czasu używam anonimowych bloków, często w głównych metodach wprowadzania kodu złomu. Ktoś inny wspomniał o tym jako narzędziu do refaktoryzacji i myślę, że to dobrze, ponieważ wynik jest pośredni i ma na celu dalszą refaktoryzację, a nie sfinalizowanie kodu. Nie pomijam całkowicie ich przydatności, ale jeśli próbujesz napisać kod, który jest powszechnie akceptowany, dobrze przetestowany i poprawny, prawie nie widzę potrzeby anonimowych bloków. Obsesja na punkcie ich używania może odwrócić uwagę od projektowania wyraźniejszych funkcji z naturalnie małymi blokami.
2

Czy istnieje przypadek, w którym zastosowanie bloków tylko w celu zmniejszenia zakresu zmiennej ma sens?

Myślę, że motywacja jest wystarczającym powodem do wprowadzenia bloku, tak.

Jak zauważył Casey, blok jest „zapachem kodu”, który wskazuje, że lepiej wyodrębnić blok do funkcji. Ale nie możesz.

John Carmark napisał notatkę na temat kodu wbudowanego w 2007 r. Podsumowując

  • Jeśli funkcja jest wywoływana tylko z jednego miejsca, rozważ wstawienie jej.
  • Jeśli funkcja jest wywoływana z wielu miejsc, sprawdź, czy możliwe jest zorganizowanie pracy do wykonania w jednym miejscu, być może z flagami, i wstaw to.
  • Jeśli istnieje wiele wersji funkcji, rozważ utworzenie pojedynczej funkcji z większą liczbą, być może domyślnie ustawionymi, parametrów.
  • Jeśli dzieło jest zbliżone do czysto funkcjonalnego, z kilkoma odniesieniami do stanu globalnego, spróbuj uczynić go całkowicie funkcjonalnym.

Pomyśl więc , dokonaj wyboru celowo i (opcjonalnie) zostaw dowód opisujący twoją motywację do dokonania wyboru.

VoiceOfUnreason
źródło
1

Moja opinia może być kontrowersyjna, ale tak, myślę, że z pewnością są sytuacje, w których użyłbym tego rodzaju stylu. Jednak „tylko ograniczenie zakresu zmiennej” nie jest poprawnym argumentem, ponieważ już to robisz. A robienie czegoś tylko po to, aby to zrobić, nie jest ważnym powodem. Zauważ też, że to wyjaśnienie nie ma znaczenia, jeśli Twój zespół już rozstrzygnął, czy tego rodzaju składnia jest konwencjonalna, czy nie.

Jestem przede wszystkim programistą C #, ale słyszałem, że w Javie zwracam hasło, char[]aby umożliwić skrócenie czasu pozostawania hasła w pamięci. W tym przykładzie nie pomoże, ponieważ śmieciarz może zbierać tablicę od momentu, gdy nie jest używana, więc nie ma znaczenia, czy hasło opuszcza zakres. Nie dyskutuję, czy opłaca się wyczyścić tablicę po jej zakończeniu, ale w tym przypadku, jeśli chcesz to zrobić, określenie zakresu zmiennej ma sens:

{
    char[] password = getPassword();
    try{
        keyStore.load(new FileInputStream(keyStoreLocation), password);
        keyManager.init(keyStore, password);
    }finally{
        Arrays.fill(password, '\0');
    }
}

Jest to bardzo podobne do instrukcji try-with-resources , ponieważ określa zasięg zasobu i wykonuje po nim finalizację. Jeszcze raz zauważ, że nie argumentuję, aby posługiwać się hasłami w ten sposób, tylko, że jeśli zdecydujesz się to zrobić, ten styl jest rozsądny.

Powodem tego jest to, że zmienna nie jest już ważna. Utworzyłeś go, użyłeś go i unieważniłeś jego stan, aby nie zawierał żadnych istotnych informacji. Nie ma sensu używać zmiennej po tym bloku, więc rozsądne jest jej zawężenie.

Innym przykładem, o którym mogę myśleć, jest sytuacja, gdy masz dwie zmienne o podobnej nazwie i znaczeniu, ale pracujesz z jedną, a potem z drugą, i chcesz je rozdzielić. Napisałem ten kod w C #:

{
    MethodBuilder m_ToString = tb.DefineMethod("ToString", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, typeofString, Type.EmptyTypes);
    var il = m_ToString.GetILGenerator();
    il.Emit(OpCodes.Ldstr, templateType.ToString()+":"+staticType.ToString());
    il.Emit(OpCodes.Ret);
    tb.DefineMethodOverride(m_ToString, m_Object_ToString);
}
{
    PropertyBuilder p_Class = tb.DefineProperty("Class", PropertyAttributes.None, typeofType, Type.EmptyTypes);
    MethodBuilder m_get_Class = tb.DefineMethod("get_Class", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, typeofType, Type.EmptyTypes);
    var il = m_get_Class.GetILGenerator();
    il.Emit(OpCodes.Ldtoken, staticType);
    il.Emit(OpCodes.Call, m_Type_GetTypeFromHandle);
    il.Emit(OpCodes.Ret);
    p_Class.SetGetMethod(m_get_Class);
    tb.DefineMethodOverride(m_get_Class, m_IPattern_get_Class);
}

Możesz argumentować, że mogę po prostu zadeklarować ILGenerator il;na początku metody, ale nie chcę również ponownie używać zmiennej dla różnych obiektów (trochę funkcjonalne podejście). W takim przypadku bloki ułatwiają rozdzielenie wykonywanych zadań, zarówno pod względem składniowym, jak i wizualnym. Mówi także, że po bloku jestem skończony ili nic nie powinno mieć do niego dostępu.

Argumentem przeciwko temu przykładowi są metody. Może tak, ale w tym przypadku kod nie jest tak długi, a rozdzielenie go na różne metody wymagałoby również przekazania wszystkich zmiennych używanych w kodzie.

IllidanS4 chce powrotu Moniki
źródło