Rozumiem znaczenie dobrze udokumentowanego kodu. Ale rozumiem również znaczenie samodokumentowania kodu. Im łatwiej jest wizualnie odczytać określoną funkcję, tym szybciej możemy przejść podczas konserwacji oprogramowania.
Powiedziawszy to, lubię rozdzielać duże funkcje na inne mniejsze. Ale robię to do tego stopnia, że klasa może mieć w górę pięć z nich tylko po to, aby obsługiwać jedną metodę publiczną. Teraz pomnóż pięć prywatnych metod przez pięć publicznych, a otrzymasz około dwudziestu pięciu ukrytych metod, które prawdopodobnie zostaną wywołane tylko raz przez te publiczne.
Jasne, teraz łatwiej jest czytać te publiczne metody, ale nie mogę przestać myśleć, że posiadanie zbyt wielu funkcji to zła praktyka.
[Edytować]
Ludzie pytają mnie, dlaczego uważam, że zbyt wiele funkcji to zła praktyka.
Prosta odpowiedź: to przeczucie.
Moje przekonanie nie jest poparte choćby godzinami doświadczenia w inżynierii oprogramowania. To tylko niepewność, która dała mi „blok pisarza”, ale dla programisty.
W przeszłości programowałem tylko projekty osobiste. Niedawno przeszedłem na projekty zespołowe. Teraz chcę się upewnić, że inni mogą czytać i rozumieć mój kod.
Nie byłem pewien, co poprawi czytelność. Z jednej strony myślałem o podzieleniu jednej dużej funkcji na inne mniejsze o zrozumiałych nazwach. Ale była też inna strona, która powiedziała, że to po prostu zbędne.
Tak więc proszę o oświecenie się, aby wybrać właściwą ścieżkę.
[Edytować]
Poniżej mam włączone dwie wersje jak ja mógłby rozwiązać mój problem. Pierwszy rozwiązuje to, nie rozdzielając dużych fragmentów kodu. Drugi robi osobne rzeczy.
Pierwsza wersja:
public static int Main()
{
// Displays the menu.
Console.WriteLine("Pick your option");
Console.Writeline("[1] Input and display a polynomial");
Console.WriteLine("[2] Add two polynomials");
Console.WriteLine("[3] Subtract two polynomials");
Console.WriteLine("[4] Differentiate two polynomials");
Console.WriteLine("[0] Quit");
}
Druga wersja:
public static int Main()
{
DisplayMenu();
}
private static void DisplayMenu()
{
Console.WriteLine("Pick your option");
Console.Writeline("[1] Input and display a polynomial");
Console.WriteLine("[2] Add two polynomials");
Console.WriteLine("[3] Subtract two polynomials");
Console.WriteLine("[4] Differentiate two polynomials");
Console.WriteLine("[0] Quit");
}
W powyższych przykładach ta ostatnia wywołuje funkcję, która będzie używana tylko raz przez cały czas działania programu.
Uwaga: powyższy kod jest uogólniony, ale ma ten sam charakter co mój problem.
Oto moje pytanie: które? Czy wybieram pierwszy, czy drugi?
Odpowiedzi:
Jest to tak zwane kapsułkowanie , które tworzy abstrakcję sterowania na wyższym poziomie. To coś dobrego.
Oznacza to, że ktoś czyta kod, gdy dotrą do
startTheEngine()
metody w kodzie, może ignorować wszystkie niższe szczegóły poziomu, takich jakopenIgnitionModule()
,turnDistributorMotor()
,sendSparksToSparkPlugs()
,injectFuelIntoCylinders()
,activateStarterSolenoid()
, i wszystkich innych skomplikowanych, małych, funkcji, które muszą być prowadzone w celu ułatwić znacznie większą, bardziej abstrakcyjną funkcjęstartTheEngine()
.O ile problem, z którym masz do czynienia w kodzie, nie dotyczy bezpośrednio jednego z tych komponentów, opiekunowie kodu mogą przejść dalej, ignorując piaskownicową, zamkniętą funkcjonalność.
Ma to również tę dodatkową zaletę, że ułatwia testowanie kodu. . Na przykład mogę napisać przypadek testowy
turnAlternatorMotor(int revolutionRate)
i przetestować jego funkcjonalność całkowicie niezależnie od innych systemów. Jeśli występuje problem z tą funkcją, a wynik nie jest tym, czego oczekuję, to wiem, na czym polega problem.Kod, który nie jest podzielony na komponenty, jest znacznie trudniejszy do przetestowania. Nagle twoi opiekunowie patrzą tylko na abstrakcję, zamiast być w stanie zagłębić się w mierzalne komponenty.
Radzę robić to, co robisz, ponieważ Twój kod będzie skalowany, łatwy w utrzymaniu i może być używany i aktualizowany przez wiele lat.
źródło
myString.replaceAll(pattern, originalData);
czy ja wklejam całą metodę replaceAll w moim kodzie, włączając w to tablicę char, która reprezentuje podstawowy obiekt ciągu za każdym razem, gdy potrzebuję korzystać z funkcjonalności ciągu?Tak i nie. Podstawową zasadą jest: metoda powinna robić jedną rzecz i tylko jedną rzecz
Poprawne jest „rozbicie” metody na mniejsze. Z drugiej strony metody te powinny optymalnie być na tyle ogólne, aby nie tylko służyły tej „dużej” metodzie, ale także by mogły być używane w innych miejscach. W niektórych przypadkach metoda będzie postępować zgodnie z prostą strukturą drzewa, na przykład metoda Initialize, która wywołuje InitializePlugins, InitializeInterface itp.
Jeśli masz naprawdę dużą metodę / klasę, jest to zwykle znak, że robi ona zbyt wiele i musisz dokonać refaktoryzacji, aby rozbić „kroplę”. Perphaps ukrywa pewną złożoność w innej klasie pod abstrakcją i stosuje wstrzykiwanie zależności
źródło
Myślę, że ogólnie lepiej jest pomylić się ze zbyt wieloma funkcjami niż za mało. Te dwa wyjątki od tej reguły widziałem w praktyce są dla DRY i YAGNI zasad. Jeśli masz wiele prawie identycznych funkcji, powinieneś je połączyć i użyć parametrów, aby uniknąć powtarzania się. Możesz także mieć zbyt wiele funkcji, jeśli zostały utworzone „na wszelki wypadek” i nie są używane. Nie widzę absolutnie nic złego w posiadaniu funkcji, która jest używana tylko raz, jeśli zwiększa czytelność i łatwość konserwacji.
źródło
Świetna odpowiedź jmort253 zainspirowała mnie do odpowiedzi „tak, ale” odpowiedź…
Posiadanie wielu małych prywatnych metod nie jest złą rzeczą, ale zwróć uwagę na to dręczące uczucie, które sprawia, że zadajesz tutaj pytanie :). Jeśli dojdziesz do momentu, w którym piszesz testy, które koncentrują się tylko na metodach prywatnych (albo przez wywołanie ich bezpośrednio, albo przez skonfigurowanie takiego scenariusza, aby wywołać go w określony sposób, abyś mógł przetestować wynik) powinieneś cofnąć się o krok.
To, co możesz mieć, to nowa klasa z własnym, bardziej skoncentrowanym interfejsem publicznym, próbującym uciec. W tym momencie możesz pomyśleć o wyodrębnieniu klasy, aby zamknąć tę część istniejącej funkcjonalności klas, która obecnie żyje metodą prywatną. Teraz twoja oryginalna klasa może wykorzystywać twoją nową klasę jako zależność i możesz pisać bardziej ukierunkowane testy bezpośrednio dla tej klasy.
źródło
Niemal każdy projekt OO, do którego dołączyłem, bardzo cierpiał z powodu zbyt dużych klas i zbyt długich metod.
Kiedy odczuwam potrzebę komentarza wyjaśniającego następną sekcję kodu, wyodrębniam tę sekcję do osobnej metody. W dłuższej perspektywie powoduje to, że kod jest krótszy. Te małe metody są ponownie wykorzystywane częściej, niż początkowo można oczekiwać. Zaczynasz widzieć możliwości zebrania kilku z nich w osobną klasę. Wkrótce myślisz o swoim problemie na wyższym poziomie, a cały wysiłek idzie znacznie szybciej. Nowe funkcje są łatwiejsze do napisania, ponieważ można budować na tych małych klasach utworzonych z tych małych metod.
źródło
Klasa z 5 metodami publicznymi i 25 metodami prywatnymi nie wydaje mi się taka duża. Upewnij się tylko, że twoje zajęcia mają jasno określone obowiązki i nie przejmuj się zbytnio liczbą metod.
To powiedziawszy, te prywatne metody powinny skupiać się na jednym konkretnym aspekcie całego zadania, ale nie w sposób „doSomethingPart1”, „doSomethingPart2”. Nic nie zyskasz, jeśli te prywatne metody są tylko fragmentami arbitralnie podzielonej długiej historii, z których każda jest bez znaczenia poza całym kontekstem.
źródło
Fragment Clean Code R. Martina (s. 176):
źródło
Niektóre opcje:
W porządku. Po prostu nazwij metody prywatne, a także nazwy metod publicznych. I dobrze nazwij swoje metody publiczne.
Wyodrębnij niektóre z tych metod pomocniczych do klas pomocniczych lub modułów pomocniczych. I upewnij się, że te klasy / moduły pomocnicze są dobrze nazwane i stanowią solidne abstrakcje same w sobie.
źródło
Nie sądzę, aby kiedykolwiek pojawiał się problem „zbyt wielu prywatnych metod”, jeśli wydaje się, że jest to prawdopodobnie objaw złej architektury kodu.
Oto, jak o tym myślę ... Jeśli architektura jest dobrze zaprojektowana, ogólnie oczywiste jest, gdzie powinien być kod X. Dopuszczalne jest kilka wyjątków od tej reguły - ale muszą one być naprawdę wyjątkowe, w przeciwnym razie zreorganizuj architekturę, aby obsługiwać je jako standard.
Jeśli problemem jest to, że po otwarciu klasy jest ona pełna prywatnych metod, które dzielą większe fragmenty na mniejsze fragmenty kodu, być może jest to objaw braku architektury. Zamiast klas i architektury ten obszar kodu został zaimplementowany w sposób proceduralny w ramach jednej klasy.
Znalezienie równowagi tutaj zależy od projektu i jego wymagań. Przeciwnym argumentem tego jest powiedzenie, że młodszy programista może podważyć rozwiązanie w 50 liniach kodu, który zajmuje architektowi 50 klas.
źródło
Tak.
W Pythonie pojęcie „prywatny” (używane przez C ++, Java i C #) tak naprawdę nie istnieje.
Istnieje konwencja nazewnictwa „w pewnym sensie prywatna”, ale to wszystko.
Prywatny może prowadzić do trudnego do przetestowania kodu. Może również złamać „zasadę otwartego i zamkniętego”, czyniąc kod po prostu zamkniętym.
W związku z tym dla osób, które korzystały z Pythona, funkcje prywatne nie mają żadnej wartości. Po prostu upublicznij wszystko i gotowe.
źródło