Czy kiedykolwiek sensowne jest, aby refaktor miał wyższy LOC? [Zamknięte]

25

Czy istnieją przypadki, w których więcej pełnych kodów (jak w bardziej logicznych instrukcjach) jest bardziej przejrzystych niż bardziej zwięzłych kodów?

zła nazwa użytkownika
źródło
18
Oczywiście, że tak. Nawet po wyeliminowaniu duplikacji kod definiujący nową wyodrębnioną funkcję zajmuje własną przestrzeń. Napisanie nowej funkcji może zająć cztery linie i zaoszczędzić tylko dwie, a mimo to być warte zachodu.
Kilian Foth,
5
„bardziej zwięzły kod”? Naprawdę nienawidzę błędnego przekonania, że ​​mniejsza liczba wierszy oznacza „lepszy” kod. Nie ma W rzeczywistości jest zwykle odwrotnie. Przychodzi moment - i osiąga się to bardzo szybko - w którym coraz więcej znaczenia w coraz mniejszej przestrzeni sprawia, że ​​kod jest trudniejszy do zrozumienia. W rzeczywistości istnieje cały konkurs - Międzynarodowy konkurs zaciemnionego kodu C - w którym wielu zwycięzców polega na tych ograniczeniach ludzkiego zrozumienia, aby pisać nieprzenikniony kod.
Andrew Henle,
1
Tytuł twojego pytania i samo pytanie zadają różne pytania. Na przykład instrukcję if można zamienić na wyrażenie trójskładnikowe, które jest logicznie takie samo, ale zawiera tylko 1 linię.
Captain Man,
1
-1 *** więcej pełnego kodu (jak w bardziej logicznych instrukcjach) *** szczegółowość i liczba instrukcji logicznych to dwie niezwiązane ze sobą rzeczy. Zła forma zmiany definicji.
Pieter B

Odpowiedzi:

70

Aby odpowiedzieć na to pytanie, weźmy przykład z prawdziwego świata, który mi się przydarzył. W bibliotece C #, którą prowadzę, miałem następujący kod:

TResult IConsFuncMatcher<T, TResult>.Result() =>
    TryCons(_enumerator) is var simpleMatchData && !simpleMatchData.head.HasValue
        ? _emptyValue.supplied
            ? _emptyValue.value
            : throw new NoMatchException("No empty clause supplied");
        : _recursiveConsTests.Any() 
            ? CalculateRecursiveResult() 
            : CalculateSimpleResult(simpleMatchData);

Dyskutując o tym z rówieśnikami, jednomyślny werdykt był taki, że zagnieżdżone wyrażenia trójskładnikowe, w połączeniu z „sprytnym” użyciem, is varskutkowały zwięzłym, ale trudnym do odczytania kodem.

Więc zmieniłem to na:

TResult IConsFuncMatcher<T, TResult>.Result()
{
    var simpleMatchData = TryCons(_enumerator);

    if (!simpleMatchData.head.HasValue)
    {
        return _emptyValue.supplied
            ? _emptyValue.value
            : throw new NoMatchException("No empty clause supplied");
    }

    return _recursiveConsTests.Any() 
        ? CalculateRecursiveResult() 
        : CalculateSimpleResult(simpleMatchData);
}

Oryginalna wersja zawierała tylko jedno wyrażenie złożone z domniemanym return. Nowa wersja zawiera teraz wyraźną deklarację zmiennej, ifinstrukcję i dwie jawne returns. Zawiera więcej instrukcji i więcej wierszy kodu. Jednak wszyscy, z którymi się konsultowałem, uważali, że łatwiej jest czytać i rozumować, co jest kluczowym aspektem „czystego kodu”.

Tak więc odpowiedź na twoje pytanie jest stanowczym „tak”, bardziej szczegółowe może być czystsze niż zwięzły kod, a zatem jest ważnym refaktoryzacją.

David Arno
źródło
34
W dzisiejszych czasach mózgi programistów są rzadszym zasobem niż dysk, procesor, pamięć RAM lub przepustowość sieci. Te inne rzeczy są ważne, a w niektórych zastosowaniach może być Twój czynnik ograniczający, ale w większości przypadków, chcesz zoptymalizować zdolność deweloperów do zrozumienia kod pierwszy , a następnie te inne rzeczy.
anaximander
2
@anaximander, Absolutnie się zgadzam. Napisz kod, aby inne osoby najpierw przeczytały, a kompilator drugi. Dlatego uważam za użyteczne, aby inni sprawdzali mój kod, nawet jeśli tylko ja go rozwijam.
David Arno,
4
Gdybym to sprawdzał, sugerowałbym odwrócenie kolejności instrukcji return i usunięcie !z warunku. Sugerowałbym również umieszczenie drugiego zwrotu w else. Ale nawet w obecnej postaci jest to ogromna poprawa.
Martin Bonner obsługuje Monikę
2
@DavidArno Widzę tę logikę, a jeśli if (!foo.HasValue)jest to idiom w twoim kodzie, jeszcze silniej. Jednak iftak naprawdę nie jest to wczesne wyjście - to „zrób to lub tamto, w zależności od”.
Martin Bonner obsługuje Monikę
2
@fabric Porównanie wartości boolowskich jest niebezpieczne. Unikam tego tak bardzo, jak potrafię.
Martin Bonner obsługuje Monikę
30

1. Brak korelacji między LOC a jakością kodu.

Celem refaktoryzacji jest poprawa jakości fragmentu kodu.

LOC jest bardzo podstawową miarą, która czasami koreluje z jakością fragmentu kodu: na przykład metoda z kilkoma tysiącami LOC może mieć problemy z jakością. Należy jednak zauważyć, że LOC nie jest jedyną miarą, aw wielu przypadkach brakuje korelacji z jakością. Na przykład metoda 4 LOC niekoniecznie jest bardziej czytelna lub łatwiejsza w utrzymaniu niż metoda 6 LOC.

2. Niektóre techniki refaktoryzacji polegają na dodaniu LOC.

Jeśli weźmiesz listę technik refaktoryzacji , możesz łatwo dostrzec te, które polegają na celowym dodaniu LOC. Przykłady:

Oba są bardzo przydatnymi technikami refaktoryzacji, a ich wpływ na LOC jest całkowicie nieistotny przy rozważaniu, czy ich użyć, czy nie.

Unikaj używania LOC.

LOC to niebezpieczna metryka. Jest bardzo łatwy do zmierzenia i bardzo trudny do prawidłowej interpretacji.

Dopóki nie zapoznasz się z technikami pomiaru jakości kodu, rozważ przede wszystkim unikanie pomiaru LOC. Przez większość czasu nie dostaniesz nic istotnego, a byłyby przypadki, w których wprowadziłoby Cię to w błąd w zakresie obniżenia jakości kodu.

Arseni Mourzenko
źródło
Ponownie przeanalizowałeś swoją odpowiedź i poprawiłeś jakość, dodając więcej LOT (wierszy tekstu): p
grinch
12

Jeśli chcesz zobaczyć ostateczny wynik minimalizacji liczby bajtów lub liczby LoC kodu źródłowego, zapoznaj się ze zgłoszeniami na stronie Stack Exchange Code Golf .

Jeśli kod źródłowy zostanie zmniejszony w taki sposób, wkrótce będziesz mieć nie do utrzymania bałagan. Nawet jeśli jesteś osobą, która napisała taki kod i rozumiesz go w tym czasie w pełni, jak skuteczna będziesz po powrocie do niego za sześć miesięcy? Nie ma dowodów na to, że tak minimalny kod faktycznie wykonuje się szybciej.

Kod powinien być napisany w taki sposób, aby każdy członek twojego zespołu mógł na niego spojrzeć i od razu zrozumieć, co robi.

Wędrowny
źródło
Może zbędne, ale żeby to przeliterować; jeśli zmienimy kod w golfa pod kątem czytelności, zawsze otrzymacie więcej LoC
JollyJoker
1

Tak, refaktoryzacja może zdecydowanie skutkować większą liczbą linii kodu.

Najczęstszym przypadkiem IMO jest pobranie kodu, który nie jest ogólny, i uczynienie go bardziej ogólnym / elastycznym . Generowanie kodu z łatwością powoduje znaczne zwiększenie wierszy kodu (czasami dwa lub więcej razy).

Jeśli spodziewasz się, że nowy kod będzie używany przez innych (zamiast tylko jako wewnętrzny komponent oprogramowania) jako bibliotekę, zwykle kończy się to dodawaniem nieprzystosowanego kodu i znaczników dokumentacji wewnątrz kodu, co ponownie zwiększy wiersze kodu.

Oto przykład bardzo częstego scenariusza występującego u każdego programisty:

  • Twój produkt potrzebuje pilnej nowej funkcji o wysokim priorytecie lub poprawki lub ulepszenia w ciągu dwóch tygodni (lub niezależnie od tego, jakie ramy czasowe są uważane za pilne dla Twojego rozmiaru projektu / wielkości firmy / itp.)
  • ciężko pracujesz i dostarczasz XYZ na czas i to działa. Gratulacje! Dobra robota!
  • Podczas opracowywania XYZ twój istniejący projekt / implementacja kodu tak naprawdę nie obsługiwał XYZ, ale byłeś w stanie umieścić XYZ w bazie kodu
  • problem polega na tym, że podkładka jest brzydka i ma okropny zapach kodu, ponieważ zrobiłeś pewne podstępne / sprytne / brzydkie / złe praktyki-ale-trochę-działa
  • kiedy znajdziesz czas później, przebudowujesz kod, który może zmienić wiele klas lub dodać nową warstwę klas, a twoje nowe rozwiązanie jest „zrobione dobrze” i nie ma już nieprzyjemnego zapachu kodu ... jednak robi to „właściwa droga” zajmuje teraz znacznie więcej linii kodu.

Kilka konkretnych przykładów, które przychodzą mi z głowy:

  • dla interfejsu wiersza poleceń możesz mieć 5000 linii kodu if / else-if lub użyć callbacków ... każde callback byłoby znacznie mniejsze i łatwiejsze do odczytania / przetestowania / weryfikacji / debugowania / itp., ale jeśli policzysz linie kod brzydkie 5000 linii kodu if / else-if prawdopodobnie byłby mniejszy
  • w przypadku fragmentu kodu obsługującego N metod przetwarzania można ponownie użyć instrukcji if / else, które wyglądałyby najbardziej brzydko ...
    • lub możesz przejść na wywołania zwrotne, które byłyby ładniejsze / lepsze, ale wywołania zwrotne zajmują więcej wierszy kodu (choć nadal kompilują czas)
    • lub możesz dalej abstrakcji i tworzyć wtyczki, które można zmienić w czasie wykonywania. wtyczki są fajne, ponieważ nie trzeba ponownie kompilować głównego produktu dla każdej nowej wtyczki lub modyfikacji istniejącej wtyczki. i możesz opublikować API, aby inni mogli rozszerzyć produkt. ALE znowu podejście oparte na wtyczkach wykorzystuje więcej linii kodu.
  • dla GUI tworzysz świetny nowy widget
    • ty lub współpracownik zauważacie, że nowy widżet byłby świetny dla XYZ i ABC, ale teraz widżet jest ściśle zintegrowany tylko do pracy dla XYZ
    • zmieniłeś widget tak, aby działał w obu przypadkach, ale teraz suma wierszy kodu rośnie
Trevor Boyd Smith
źródło