Zgodnie z przyjętą odpowiedzią „ Racjonalne uzasadnienie preferowania zmiennych lokalnych zamiast zmiennych instancji? ” Zmienne powinny mieć możliwie najmniejszy zakres.
Uprość problem do mojej interpretacji, oznacza to, że powinniśmy zmienić kod tego rodzaju:
public class Main {
private A a;
private B b;
public ABResult getResult() {
getA();
getB();
return ABFactory.mix(a, b);
}
private getA() {
a = SomeFactory.getA();
}
private getB() {
b = SomeFactory.getB();
}
}
w coś takiego:
public class Main {
public ABResult getResult() {
A a = getA();
B b = getB();
return ABFactory.mix(a, b);
}
private getA() {
return SomeFactory.getA();
}
private getB() {
return SomeFactory.getB();
}
}
ale zgodnie z „duchem” zmiennych zmienne powinny żyć w najmniejszym możliwym zakresie, czy „nigdy nie mieć zmiennych” ma mniejszy zakres niż „mieć zmiennych”? Myślę więc, że powyższa wersja powinna zostać refaktoryzowana:
public class Main {
public ABResult getResult() {
return ABFactory.mix(getA(), getB());
}
private getA() {
return SomeFactory.getA();
}
private getB() {
return SomeFactory.getB();
}
}
więc getResult()
nie ma żadnych lokalnych zmiennych. Czy to prawda?
refactoring
scope
local-variable
ocomfd
źródło
źródło
final
słowa kluczowego, czy nie.Odpowiedzi:
Nie. Jest kilka powodów, dla których:
I tak dalej.
źródło
var taxIndex = getTaxIndex();
).now()
) Usunięcie zmiennej i wywołanie metody więcej niż jeden raz może spowodować błędy. Może to stworzyć sytuację, która jest naprawdę subtelna i trudna do debugowania. Może się to wydawać oczywiste, ale jeśli masz ślepą misję refaktoryzacji w celu usunięcia zmiennych, łatwo jest wprowadzić wady.Zgadzam się, należy unikać zmiennych, które nie są konieczne i nie poprawiają czytelności kodu. Im więcej zmiennych znajduje się w danym punkcie kodu, tym bardziej skomplikowany jest ten kod.
Naprawdę nie widzę korzyści ze zmiennych
a
ib
w twoim przykładzie, więc napisałbym wersję bez zmiennych. Z drugiej strony funkcja jest tak prosta, że nie sądzę, żeby miała to duże znaczenie.Staje się to coraz większym problemem, im dłużej funkcja jest dostępna, tym więcej zmiennych ma zasięg.
Na przykład jeśli masz
Na górze większej funkcji zwiększasz obciążenie psychiczne związane z rozumieniem reszty kodu, wprowadzając trzy zmienne zamiast jednej. Musisz przeczytać resztę kodu, aby zobaczyć,
a
czyb
są ponownie używane. Miejscowi, których zasięg jest dłuższy niż potrzeba, są niekorzystni dla ogólnej czytelności.Oczywiście w przypadku, gdy zmienna jest to konieczne (np przechowywanie tymczasowy rezultat) lub gdy zmienna ma poprawić czytelność kodu, to powinny być przechowywane.
źródło
var result = getResult(...); return result;
to, że można nałożyć punkt przerwaniareturn
i dowiedzieć się, co dokładnieresult
jest.Oprócz innych odpowiedzi chciałbym wskazać coś jeszcze. Korzyścią z utrzymywania małego zakresu zmiennej jest nie tylko zmniejszenie ilości kodu dostępnego do zmiennej, ale także zmniejszenie liczby możliwych ścieżek kontroli, które mogą potencjalnie zmodyfikować zmienną (przez przypisanie jej nowej wartości lub wywołanie metoda mutacji na istniejącym obiekcie przechowywanym w zmiennej).
Zmienne o zasięgu klasowym (instancja lub statyczne) mają znacznie więcej możliwych ścieżek kontroli niż zmienne o zasięgu lokalnym, ponieważ można je mutować metodami, które można wywoływać w dowolnej kolejności, dowolną liczbę razy, a często także kodem spoza klasy .
Rzućmy okiem na twoją początkową
getResult
metodę:Teraz nazwy
getA
igetB
mogą sugerować , że zostaną przypisane dothis.a
ithis.b
, nie możemy na pewno wiedzieć po samym spojrzeniugetResult
. Dlatego możliwe jest, że wartościthis.a
ithis.b
przekazane domix
metody zamiast tego pochodzą ze stanuthis
obiektugetResult
, który został wcześniej wywołany, co jest niemożliwe do przewidzenia, ponieważ klienci kontrolują sposób i czas wywoływania metod.W zmienionym kodzie z lokalnymi
a
ib
zmiennymi jest jasne, że istnieje dokładnie jeden (wolny od wyjątków) przepływ kontrolny od przypisania każdej zmiennej do jej użycia, ponieważ zmienne są deklarowane tuż przed ich użyciem.Zatem znaczną zaletą jest przenoszenie (modyfikowalne) zmiennych z zakresu klasy do zakresu lokalnego (jak również przenoszenie (modyfikowalne) zmiennych z zewnątrz pętli do wewnątrz), ponieważ upraszcza rozumowanie przepływu sterowania.
Z drugiej strony wyeliminowanie zmiennych jak w poprzednim przykładzie ma mniejszą zaletę, ponieważ tak naprawdę nie wpływa na rozumowanie przepływu sterowania. Tracisz także nazwy nadane wartościom, co nie zdarza się, gdy po prostu przenosisz zmienną do zakresu wewnętrznego. Jest to kompromis, który należy wziąć pod uwagę, więc eliminacja zmiennych może być lepsza w niektórych przypadkach, a gorsza w innych.
Jeśli nie chcesz stracić nazw zmiennych, ale nadal chcesz zmniejszyć zakres zmiennych (w przypadku ich użycia w większej funkcji), możesz rozważyć zawarcie zmiennych i ich zastosowanie w instrukcji blokowej ( lub przeniesienie ich do własnej funkcji ).
źródło
Jest to w pewnym stopniu zależne od języka, ale powiedziałbym, że jedną z mniej oczywistych korzyści programowania funkcjonalnego jest to, że zachęca programistę i czytnik kodu do ich niepotrzebowania. Rozważać:
Lub trochę LINQ:
Lub Node.js:
Ostatni to łańcuch wywoływania funkcji na wyniku poprzedniej funkcji, bez żadnych zmiennych pośrednich. Wprowadzenie ich uczyniłoby to o wiele mniej jasne.
Różnica między pierwszym przykładem a pozostałymi dwoma polega jednak na domyślnej kolejności działania . Może nie być to ta sama kolejność, w jakiej jest obliczana, ale to kolejność, w której czytelnik powinien o tym pomyśleć. W przypadku dwóch drugich jest to od lewej do prawej. W przykładzie Lisp / Clojure jest bardziej jak od prawej do lewej. Powinieneś być ostrożny w pisaniu kodu, który nie jest w „domyślnym kierunku” dla twojego języka, i zdecydowanie należy unikać wyrażeń „wyodrębnionych”, które łączą oba te języki.
Operator potoku F #
|>
jest przydatny częściowo, ponieważ pozwala pisać rzeczy od lewej do prawej, które w przeciwnym razie musiałyby być od prawej do lewej.źródło
myCollection.Select(_ => _.SomeProp).Where(_ => _.Size > 4);
Powiedziałbym „nie”, ponieważ powinieneś przeczytać „najmniejszy możliwy zakres” jako „spośród istniejących zakresów lub tych, które można dodać”. W przeciwnym razie sugerowałoby to, że powinieneś tworzyć sztuczne zakresy (np. Nieuzasadnione
{}
bloki w językach podobnych do C), aby upewnić się, że zakres zmiennej nie wykracza poza ostatnie zamierzone użycie, i że byłoby to ogólnie odrzucone jako zaciemnienie / bałagan, chyba że już istnieje dobry powód, aby zakres istniał niezależnie.źródło
Rozważ funkcje ( metody ). Nie dzieli ani kodu na najmniejszą możliwą podzadanie, ani największego pojedynczego fragmentu kodu.
Jest to limit przesuwania z ograniczeniem logicznych zadań na części eksploatacyjne.
To samo dotyczy zmiennych . Wskazanie logicznych struktur danych na zrozumiałe części. Lub po prostu nazwij (określając) parametry:
Ale oczywiście mając na górze deklarację i dwieście linii dalej pierwsze użycie jest obecnie akceptowane jako zły styl. To jest właśnie to, co „zmienne powinny żyć w możliwie najmniejszym zakresie” . Jak bardzo blisko „nie używaj ponownie zmiennych”.
źródło
Tym, czego brakuje jako przyczyny NIE, jest debugowanie / zdolność do czytania. Kod powinien być w tym celu zoptymalizowany, a jasne i zwięzłe nazwy bardzo pomagają, np. Wyobraź sobie trójdrożny
ten wiersz jest krótki, ale już trudny do odczytania. Dodaj jeszcze kilka parametrów, a jeśli obejmuje wiele linii.
Dla mnie ten sposób jest łatwiejszy do odczytania i przekazania znaczenia - więc nie mam problemu ze zmiennymi pośrednimi.
Innym przykładem mogą być języki takie jak R, gdzie ostatnia linia jest automatycznie wartością zwracaną:
jest to niebezpieczne, czy zwrot jest uzasadniony czy potrzebny? to jest jaśniejsze:
Jak zawsze jest to wezwanie do oceny - wyeliminuj zmienne pośrednie, jeśli nie poprawią czytania, w przeciwnym razie zachowaj je lub wprowadź.
Kolejną kwestią może być debuggowanie: jeśli wyniki pośredników są interesujące, najlepiej po prostu wprowadzić pośrednika, tak jak w powyższym przykładzie R. Częstotliwość wywoływania tego jest trudna do wyobrażenia i uważaj na to, co się rejestruje - zbyt wiele zmiennych debugujących jest mylące - ponownie wezwanie do oceny.
źródło
Odnosząc się tylko do twojego tytułu: absolutnie, jeśli zmienna nie jest potrzebna, należy ją usunąć.
Ale „niepotrzebne” nie oznacza, że można napisać równoważny program bez użycia zmiennej, w przeciwnym razie powiedziano by nam, że powinniśmy pisać wszystko w formacie binarnym.
Najczęstszym rodzajem niepotrzebnej zmiennej jest nieużywana zmienna, im mniejszy zakres zmiennej, tym łatwiej jest ustalić, że jest niepotrzebna. Trudniej jest ustalić, czy zmienna pośrednia jest niepotrzebna, ponieważ nie jest to sytuacja binarna, lecz kontekstowa. W rzeczywistości identyczny kod źródłowy w dwóch różnych metodach może dać inną odpowiedź tego samego użytkownika, w zależności od wcześniejszych problemów z naprawieniem otaczającego kodu.
Jeśli twój przykładowy kod był dokładnie taki, jak przedstawiono, sugerowałbym pozbycie się dwóch prywatnych metod, ale nie miałbym najmniejszej obawy, czy zapisałeś wynik wywołań fabrycznych do zmiennej lokalnej, czy po prostu użyłeś ich jako argumentów do miksu metoda.
Czytelność kodu przebija wszystko oprócz poprawnego działania (poprawnie obejmuje dopuszczalne kryteria wydajności, które rzadko „są tak szybkie, jak to możliwe”).
źródło