Rozmawiałem ze współpracownikiem na temat złamania return
instrukcji i instrukcji, która oblicza wartość zwracaną w dwóch wierszach.
Na przykład
private string GetFormattedValue()
{
var formattedString = format != null ? string.Format(format, value) : value.ToString();
return formattedString;
}
zamiast
private string GetFormattedValue()
{
return format != null ? string.Format(format, value) : value.ToString();
}
Pod względem kodowym tak naprawdę nie widzę wartości w pierwszym wariancie. Dla mnie ta ostatnia jest bardziej przejrzysta, szczególnie w przypadku tak krótkich metod. Jego argumentem było to, że poprzedni wariant jest łatwiejszy do debugowania - co jest dość małą zaletą, ponieważ VisualStudio pozwala nam na bardzo szczegółową kontrolę instrukcji, gdy wykonanie jest zatrzymane z powodu punktu przerwania.
Moje pytanie brzmi: czy nadal jest poprawny punkt do pisania kodu mniej wyraźnego, tylko po to, aby ułatwić debugowanie rzutu oka? Czy są jakieś inne argumenty przemawiające za wariantem z kalkulacją i return
wyciągiem podzielonym ?
źródło
private string GetFormattedValue() => string.Format(format ?? "{0}", value);
Odpowiedzi:
Wprowadzenie zmiennych objaśniających jest dobrze znanym refaktoryzowaniem, które może czasem pomóc w zwiększeniu czytelności skomplikowanych wyrażeń. Jednak w pokazanym przypadku
Co więcej, nowsze wersje debugera Visual Studio mogą pokazywać wartość zwracaną przez funkcję w większości przypadków bez wprowadzania zbędnej zmiennej (ale uwaga, są pewne zastrzeżenia, spójrz na ten starszy post SO i różne odpowiedzi ).
Tak więc w tym konkretnym przypadku zgadzam się z tobą, jednak istnieją inne przypadki, w których zmienna wyjaśniająca może rzeczywiście poprawić jakość kodu.
źródło
result
jako nazwy zmiennej. Nie tyle dłużej i łatwiej debugowaćresult
często, powoduje po prostu szum w kodzie i rzadko zwiększa czytelność, co jest dokładnie celem mojej odpowiedzi. Można to usprawiedliwić w kontekście, w którym pomaga to w debugowaniu, ale unikałbym tego podczas korzystania z debugera, który nie potrzebuje oddzielnej zmiennej.result
przekazuje informację, że jest to wartość wynikająca z funkcji, dzięki czemu można na nią spojrzeć przed powrotem funkcji.Biorąc pod uwagę fakty, które:
a) Nie ma wpływu na końcowy kod, ponieważ kompilator optymalizuje zmienną.
b) Oddzielne rozszerzenie zwiększa możliwości debugowania.
Osobiście doszedłem do wniosku, że dobrą praktyką jest ich rozdzielanie w 99% przypadków.
Robienie tego w ten sposób nie ma istotnych wad. Argument, że przesadza kod, jest błędny, ponieważ rozdęty kod jest trywialnym problemem w porównaniu z nieczytelnym lub trudnym do debugowania kodem. Co więcej, ta metoda sama w sobie nie może tworzyć mylącego kodu, co całkowicie zależy od programisty.
źródło
Często wprowadzenie zmiennej tylko w celu nazwania jakiegoś wyniku jest bardzo pomocne, gdy sprawia, że kod jest bardziej samodokumentujący. W tym przypadku nie jest to czynnik, ponieważ nazwa zmiennej jest bardzo podobna do nazwy metody.
Zauważ, że metody jednej linii nie mają żadnej wartości nieodłącznej. Jeśli zmiana wprowadza więcej wierszy, ale kod jest jaśniejszy, to dobra zmiana.
Ale ogólnie rzecz biorąc, decyzje te są wysoce zależne od osobistych preferencji. Np. Uważam oba rozwiązania za mylące, ponieważ operator warunkowy jest używany niepotrzebnie. Wolałbym instrukcję if. Ale w swoim zespole mogłeś zgodzić się na różne konwencje. Następnie rób wszystko, co sugerują twoje konwencje. Jeśli konwencje milczą w takim przypadku, zauważ, że jest to bardzo niewielka zmiana, która nie ma znaczenia na dłuższą metę. Jeśli ten wzorzec występuje wielokrotnie, być może zainicjuj dyskusję na temat tego, jak Ty jako zespół chcesz poradzić sobie z tymi przypadkami. Ale to dzieli włosy na „dobry kod” i „może odrobinę lepszy kod”.
źródło
?
i:
używamif() {
i} else {
- - - -\\ :)
W odpowiedzi na twoje pytania:
Moje pytanie brzmi: czy nadal jest poprawny punkt do pisania kodu mniej wyraźnego, tylko po to, aby ułatwić debugowanie rzutu oka?
Tak. W rzeczywistości część twojego wcześniejszego stwierdzenia wydaje mi się (bez obrazy) trochę krótkowzroczna (patrz pogrubienie poniżej). „ Jego argumentem było to, że poprzedni wariant jest łatwiejszy do debugowania - co jest dość małą zaletą , ponieważ VisualStudio pozwala nam na bardzo szczegółową kontrolę wyciągów, gdy wykonanie jest zatrzymane z powodu punktu przerwania. ”
Ułatwienie debugowania nigdy (prawie) nigdy nie ma „ małej wartości ”, ponieważ według niektórych szacunków 50% czasu programisty spędza na debugowaniu ( Reversible Debugging Software ).
Czy są jakieś dalsze argumenty dla wariantu z obliczeniem podziału i instrukcją return?
Tak. Niektórzy programiści twierdzą, że obliczenia podziału są łatwiejsze do odczytania. To oczywiście pomaga w debugowaniu, ale także pomaga, gdy ktoś próbuje odszyfrować wszelkie reguły biznesowe, które Twój kod może wykonać lub zastosować.
UWAGA: Reguły biznesowe mogą być lepiej obsługiwane w bazie danych, ponieważ mogą się często zmieniać. Niemniej jednak jasne kodowanie w tym obszarze jest nadal najważniejsze. ( Jak zbudować silnik reguł biznesowych )
źródło
Poszedłbym jeszcze dalej:
Czemu?
Używanie operatorów trójskładnikowych do bardziej skomplikowanej logiki byłoby nieczytelne, więc można użyć stylu podobnego do powyższego w przypadku bardziej złożonych instrukcji. Używając zawsze tego stylu, kod jest spójny i łatwiejszy do przeanalizowania przez człowieka. Dodatkowo, wprowadzając tego rodzaju spójność (i używając wskazówek do kodu i innych testów) możesz uniknąć
goto fail
błędów pisowni .Kolejną zaletą jest to, że raport pokrycia kodu poinformuje cię, jeśli zapomniałeś dołączyć test dla
format
nie jest zerowy. Nie byłoby tak w przypadku operatora trójskładnikowego.Moja preferowana alternatywa - jeśli jesteś w tłumie „uzyskaj jak najszybszy zwrot”, a nie przeciwko wielokrotnym zwrotom z metody:
Możesz więc spojrzeć na ostatni powrót, aby zobaczyć, jaka jest wartość domyślna.
Ważne jest, aby być konsekwentnym - i aby wszystkie metody były zgodne z jedną lub drugą konwencją.
źródło
value.ToString()
jest niepotrzebnie wywoływany, gdy format ma wartość inną niż null. W ogólnym przypadku może to obejmować nietrywialne obliczenia i może potrwać dłużej niż wersja zawierająca ciąg formatu. Rozważmy na przykład a,value
który przechowuje PI do miliona miejsc po przecinku, i ciąg formatu, który żąda tylko kilku pierwszych cyfr.private string GetFormattedValue() => string.Format(format ?? "{0}", value);
Ten sam wpływ i użyj testów jednostkowych, aby zapewnić poprawność, zamiast polegać na debuggerze.Nie sądzę, że taką technikę można uzasadnić potrzebą debugowania. Sam spotkałem się z tym podejściem tysiąc razy i od czasu do czasu to robię, ale zawsze pamiętam, co Martin Fowler powiedział o debugowaniu :
źródło
Myślę, że niektórzy ludzie rozwieszają się w kwestiach związanych z pytaniem, takich jak operator trójskładnikowy. Tak, wiele osób go nienawidzi, więc może i tak warto to wychowywać.
Jeśli chodzi o temat pytania, przeniesienie zwróconej instrukcji do odniesienia przez zmienną ...
To pytanie zawiera 2 założenia, z którymi się nie zgadzam:
Że drugi wariant jest bardziej przejrzysty lub czytelny (mówię, że jest odwrotnie), i
że wszyscy używają Visual Studio. Korzystałem z programu Visual Studio wiele razy i mogę go używać dobrze, ale zwykle używam czegoś innego. Sceptyczne jest środowisko deweloperskie, które wymusza określone IDE.
Podział czegoś na nazwaną zmienną rzadko sprawia, że coś jest trudniejsze do odczytania, prawie zawsze robi to odwrotnie. Specyficzny sposób, w jaki ktoś to robi, może powodować problemy, na przykład, jeśli robi
var thisVariableIsTheFormattedResultAndWillBeTheReturnValue = ...
to sam dokumentalista, to oczywiście źle, ale to osobny problem.var formattedText = ...
jest w porządku.W tym konkretnym przypadku i prawdopodobnie w wielu przypadkach, ponieważ mówimy o liniach 1-liniowych, zmienna nie powiedziałaby wiele, że nazwa funkcji jeszcze nie mówi. Dlatego zmienna nie dodaje tyle. Argument debugowania nadal może się utrzymywać, ale ponownie, w tym konkretnym przypadku nie widzę niczego, co mogłoby być twoim celem podczas debugowania, i zawsze można go łatwo zmienić później, jeśli ktoś potrzebuje tego formatu do debugowania lub cokolwiek innego.
Ogólnie rzecz biorąc, i poprosiłeś o ogólną zasadę (twój przykład był po prostu przykładem formy uogólnionej), wszystkie punkty przedstawione na korzyść wariantu 1 (2-liniowego) są poprawne. Są to dobre wytyczne. Ale wytyczne muszą być elastyczne. Na przykład projekt, nad którym pracuję, ma teraz maksymalnie 80 znaków na linię, więc dzielę wiele linii, ale zwykle znajduję linie 81-85 znaków, które trudno byłoby podzielić lub zmniejszyć czytelność i pozostawiam je ponad limit.
Ponieważ jest mało prawdopodobne, aby dodać wartość, nie zrobiłbym 2 linii dla podanego konkretnego przykładu. Zrobiłbym wariant 2 (1-liniowy), ponieważ punkty nie są wystarczająco mocne, aby zrobić inaczej w tym przypadku.
źródło