Czy funkcja może być za krótka?

125

Ilekroć napotykam, że piszę tę samą logikę więcej niż raz, zwykle umieszczam ją w funkcji, więc w mojej aplikacji jest tylko jedno miejsce, w którym muszę ją zachować. Efektem ubocznym jest to, że czasami mam jedną lub dwie funkcje liniowe, takie jak:

function conditionMet(){
   return x == condition;
}

LUB

function runCallback(callback){
   if($.isFunction(callback))
     callback();
}

Czy to leniwa czy zła praktyka? Pytam tylko dlatego, że skutkuje to większą liczbą wywołań funkcji dla bardzo małych elementów logiki.

Mark Brown
źródło
3
Nie, nie za krótko. W języku C # zrobiłbym „warunek spełniony” jako właściwość, która jest elegancka i rozważałaby użycie Assert.AreEqual<int>(expected, actual, message, arg1, arg2, arg3, ...);. Drugi jest w porządku, jak jest. Potencjalnie dołączę opcjonalną flagę bool, która decyduje, czy zgłosić wyjątek / etc. w przypadku, gdy oddzwonienie nie jest funkcją.
Job
38
Tak, tylko w jednym przypadku: function myFunction () {}
Spooks
5
def yes(): return 'yes'
dietbuddha,
3
@Spooks - Tutaj dzielę włosy, ale puste funkcje są ważne przynajmniej dla klas adapterów, np. MouseMotionAdapter w download.oracle.com/javase/6/docs/api/java/awt/event/… . Oczywiście jest to tylko sposób na obejście ograniczeń językowych.
Aleksi Yrttiaho
3
@dietbuddha: Wymagania właśnie się zmieniły - teraz musi obsługiwać dwa języki w wersji demonstracyjnej w przyszłym tygodniu. Facet, który napisał „def yes ()”, był bardzo zadowolony, że zmienił go na „def yes (: (IsFrench ()?„ Oui ”:„ Yes ”)”
Andrew Shepherd

Odpowiedzi:

167

Hehe, oh Panie Brown, gdybym tylko mógł przekonać wszystkich programistów, których spotykam, do utrzymania ich tak małych funkcji, uwierzcie mi, świat oprogramowania byłby lepszym miejscem!

1) Czytelność kodu zwiększa się dziesięciokrotnie.

2) Tak łatwe do zrozumienia procesu kodu ze względu na czytelność.

3) OSUSZANIE - nie powtarzaj się - dostosowujesz się do tego bardzo dobrze!

4) Testowalne. Małe funkcje są milion razy łatwiejsze do przetestowania niż te 200 metod liniowych, które widzimy zdecydowanie za często.

Aha i nie martw się o „przeskakiwanie funkcji” pod względem wydajności. Kompilacje „Release” i optymalizacje kompilatora dbają o to bardzo dobrze, a wydajność wynosi 99% czasu w innym miejscu w projektowaniu systemów.

Czy to jest leniwe? - Bardzo odwrotnie!

Czy to zła praktyka? - Absolutnie nie. Lepiej wyciągnąć ten sposób tworzenia metod niż smoły smoliste lub „Boskie Przedmioty”, które są zbyt powszechne.

Kontynuujcie dobra prace, mój kolego rzemieślniku;)

Martin Blore
źródło
40
Nie odpowiadasz na pytanie, tylko głosisz zasadę, której bardziej ekstremalne zastosowania są tutaj kwestionowane.
4
Rzadko zdarza mi się, że ograniczenie tylko jednego głosowania jest frustrujące. Ale dla tej odpowiedzi +1 wydaje się nie oddawać sprawiedliwości.
Bevan
4
+5 ... och, może zrobić tylko +1, och cóż! Aby wesprzeć MeshMan, sugeruję następującą książkę autorstwa Roberta C. Martina Kod Clean . Ta książka naprawdę zmienia sposób, w jaki teraz programuję.
Eric-Karl
10
Bardzo sympatyczna odpowiedź, ale nie zgadzam się z większością z nich: 1) Niezwykle małe funkcje dają w sumie więcej kodu, z powodu całej hydrauliki niezbędnej dla każdej funkcji. Może to zmniejszyć czytelność, szczególnie w przypadku naprawdę dużego projektu. Również zależne od języka. 2) Zależy całkowicie od punktu 1, a zatem nie ma nic wspólnego z pytaniem. 3) Co? runCallback czterokrotnie powtarza „callback”, a trzy razy odnosi się do tej samej zmiennej w celu uruchomienia jednej funkcji. 4) Ponad 200 metod linii nie można oczywiście przetestować, ale droga do jednej linii jest długa.
l0b0
10
-1: Najbardziej przerażająca jest tutaj liczba głosów oddanych na tę odpowiedź.
Luca,
64

Powiedziałbym, że refaktoryzowana metoda jest zbyt krótka, jeśli:

  • Duplikuje operację prymitywną wyłącznie w celu uczynienia z niej metody:

Dawny:

boolean isNotValue() {
   return !isValue();
}

lub...

  • Kod jest używany tylko raz, a jego przeznaczenie jest łatwe do zrozumienia na pierwszy rzut oka.

Dawny:

void showDialog() {
    Dialog singleUseDialog = new ModalDialog();
    configureDialog(singleUseDialog);
    singleUseDialog.show();
}

void configureDialog(Dialog singleUseDialog) {
    singleUseDialog.setDimensions(400, 300);
}

Może to być prawidłowy wzorzec, ale w tym przykładzie po prostu wstawiłbym metodę configureDialog (), chyba że zamierzałem go zastąpić lub użyć tego kodu w innym miejscu.

RMorrisey
źródło
7
Oddzielenie metody jednowierszowej w drugim przypadku może być korzystne dla utrzymania spójności poziomu abstrakcji metod wywołujących. Podany przykład znajduje się na ogrodzeniu, jeśli zastosowane jest takie rozumowanie: czy dokładna szerokość i wysokość pikseli jest zbyt szczegółowa, aby można było wyświetlić okno dialogowe?
Aleksi Yrttiaho
2
Metoda „isNotValue ()” ma niepoprawną nazwę i należy ją ponownie przeredagować, aby podać nazwę rzeczywistego pytania o domenę.
4
@Aleksi: Nie zgadzam się; szczegóły są już ukryte w metodzie showDialog () w tym przykładzie; nie ma potrzeby podwójnego refaktoryzacji. Ten przykład ma na celu pokazać izolowany przypadek użycia; jeśli wzorzec jest potrzebny z różnymi konfiguracjami okien dialogowych w różnych przypadkach, sensowne byłoby cofnięcie się i refaktoryzacja po ustaleniu wzorca.
RMorrisey,
1
@Thorbjorn: Widziałem uzasadnienie, gdy odpowiadasz na pytanie dotyczące domeny biznesowej, którego logika może nie być oczywista. Po prostu wskazuję, że są - niektóre - przypadki, w których jest to przesada.
RMorrisey,
3
Być może jest to podstępne, ale argumentowałbym, że w twoich przykładach problemem nie jest to, że funkcja jest zbyt krótka, ale że nie dodają wartości opisowej.
keppla
59

Czy funkcja może być za krótka? Ogólnie nie.

W rzeczywistości jedyny sposób, aby zapewnić, że:

  1. Znalazłeś wszystkie klasy w swoim projekcie
  2. Twoje funkcje wykonują tylko jedną rzecz.

Utrzymuje twoje funkcje tak małe jak to możliwe. Lub innymi słowy, wyodrębnij funkcje ze swoich funkcji, dopóki nie będziesz mógł już wyodrębnić. Nazywam to „Wyciągaj, aż upadniesz”.

Aby to wyjaśnić: funkcja to zakres z fragmentami funkcji komunikującymi się za pomocą zmiennych. Klasa jest także zakresem z dużą ilością funkcji komunikujących się za pomocą zmiennych. Tak więc długą funkcję można zawsze zastąpić jedną lub większą liczbą klas za pomocą małej metody.

Ponadto funkcja, która jest wystarczająco duża, aby umożliwić wyodrębnienie z niej innej funkcji, z definicji robi więcej niż jedną rzecz . Jeśli więc możesz wyodrębnić funkcję z innej, powinieneś ją wyodrębnić.

Niektórzy martwią się, że doprowadzi to do rozprzestrzeniania się funkcji. Mają rację. To będzie. To właściwie dobra rzecz. To dobrze, ponieważ funkcje mają nazwy. Jeśli ostrożnie wybierasz dobre imiona, funkcje te działają jak posty, które kierują innymi osobami przez Twój kod. Rzeczywiście, dobrze nazwane funkcje wewnątrz dobrze nazwanych klas wewnątrz dobrze nazwanych przestrzeni nazw są jednym z najlepszych sposobów, aby upewnić się, że czytelnicy NIE zgubią się.

Jest o wiele więcej na ten temat w odcinku III Czystego kodu na cleancoders.com

Wujek Bob.
źródło
5
Wujek Bob! Świetnie cię tu widzieć na pokładzie. Zgodnie z oczekiwaniami, fantastyczna rada. Nigdy wcześniej nie słyszałem terminu „Wyciągaj, aż upuścisz” i na pewno będę go używać w biurze w poniedziałek;).
Martin Blore,
1
Czy byłbyś skłonny rozwinąć swój punkt 1? Wyjaśnienie tego na przykładzie byłoby świetne.
Daniel Kaplan
53

Wow, większość z tych odpowiedzi wcale nie jest bardzo pomocna.

Nie należy pisać funkcji, których tożsamość jest jej definicją . Oznacza to, że jeśli nazwa funkcji to po prostu blok kodu funkcji napisany w języku angielskim, nie zapisuj jej jako funkcji.

Rozważ swoją funkcję conditionMeti tę inną funkcję addOne(wybacz mi mój zardzewiały JavaScript):

function conditionMet() { return x == condition; }

function addOne(x) { return x + 1; }

conditionMetjest właściwą definicją pojęciową; addOnejest tautologią . conditionMetjest dobre, ponieważ nie wiesz, co się z tym conditionMetwiąże, mówiąc „warunek spełniony”, ale możesz zrozumieć, dlaczego addOneto głupie, jeśli przeczytasz to po angielsku:

"For the condition to be met is for x to equal condition" <-- explanatory! meaningful! useful!

"To add one to x is to take x and add one." <-- wtf!

Z miłości do wszystkiego, co może być jeszcze święte, proszę, nie pisz funkcji tautologicznych!

(I z tego samego powodu nie pisz komentarzy dla każdego wiersza kodu !)

Rei Miyasaka
źródło
Niektóre bardziej złożone konstrukcje językowe mogą być nieznane lub trudne do odczytania, co czyni tę użyteczną sztuczkę.
3
Zgadzając się, to, co dokładnie powinno zostać przekształcone w funkcję, zależy w dużej mierze od języka, w którym jest napisane. Funkcja taka jak addOne byłaby przydatna, gdybyś był w języku takim jak VHDL, w którym faktycznie definiujesz znaczenie dodawania jednego do poziom binarny lub teorii liczb. Chciałbym myśleć, że żaden język nie jest tak tajemniczy, że trudno go odczytać nawet dla doświadczonych programistów (może brainf # ck), ale w takim przypadku ten sam pomysł byłby słuszny, ponieważ funkcja ta jest faktycznie tłumaczeniem z angielskiego do tego języka - więc nazwa nie jest taka sama jak definicja.
Rei Miyasaka,
1
Mówię o funkcji tożsamości rzeczy ze standardowej biblioteki Haskell - nie sądzę, że można uzyskać więcej „tautologicznych” niż to :)
hugomg 12.11.11
1
@missingno Bah, to oszustwo: D
Rei Miyasaka
5
Jeśli nazwiesz funkcje według celu zamiast ich zawartości, natychmiast przestaną być głupie. Tak więc twoje przykłady mogą być np. Be function nametoolong() { return name.size > 15; }lub function successorIndex(x) { return x + 1; } Tak. Problem z twoimi funkcjami jest taki, że ich nazwa jest zła.
Hans-Peter Störr
14

Powiedziałbym, że jeśli uważasz, że zamiar jakiegoś kodu można poprawić poprzez dodanie komentarza, to zamiast dodawać ten komentarz, wypakuj kod do własnej metody. Bez względu na to, jak mały był kod.

Na przykład, jeśli twój kod miałby wyglądać tak:

if x == 1 { ... } // is pixel on?

zamiast tego wyglądać tak:

if pixelOn() { ... }

z

function pixelOn() { return x == 1; }

Innymi słowy, nie chodzi o długość metody, ale o kod samodokumentujący.

Julio
źródło
5
Mój szanowany kolega wskazuje, że można również pisać if x == PIXEL_ON. Używanie stałej może być tak samo opisowe jak użycie metody i jest trochę krótsze.
Tom Anderson
7

Myślę, że właśnie to chcesz zrobić. W tej chwili ta funkcja może składać się tylko z jednej lub dwóch linii, ale z czasem może rosnąć. Również posiadanie większej liczby wywołań funkcji pozwala odczytać wywołania funkcji i zrozumieć, co się tam dzieje. To sprawia, że ​​twój kod jest bardzo SUCHY (Don't Repeat Yourself), co jest znacznie łatwiejsze w utrzymaniu.

mpenrow
źródło
4
Więc co jest złego w uwzględnianiu tego później, kiedy możesz udowodnić, że potrzebujesz, zamiast teraz, kiedy to tylko ciężar własny?
dsimcha
Funkcje nie rosną same z siebie. IMHO przykłady są kiepskie. conditionMetjest zbyt ogólną nazwą i nie przyjmuje argumentów, więc testuje jakiś stan? (x == xWarunek) występuje w większości języków i sytuacji tak wyrazistych jak „xWarunek”.
użytkownik nieznany
Tak i dlatego chcesz mieć 75% + narzut w swoim kodzie? Singe liner napisany jako 3 linie to w rzeczywistości 300%.
Koder,
5

Zgadzam się ze wszystkimi pozostałymi postami, które widziałem. To jest dobry styl.

Narzut związany z tak małą metodą może być zerowy, ponieważ optymalizator może zoptymalizować połączenie i wstawić kod. Prosty kod taki jak ten pozwala optymalizatorowi wykonać najlepszą pracę.

Kod powinien być napisany dla jasności i prostoty. Staram się ograniczyć metodę do jednej z dwóch ról: podejmowanie decyzji; lub wykonując pracę. Może to generować metody jednowierszowe. Im lepiej to robię, tym lepiej znajduję mój kod.

Taki kod ma zwykle wysoką spójność i niskie sprzężenie, co jest dobrą praktyką kodowania.

EDYCJA: Uwaga na temat nazw metod. Użyj nazwy metody, która wskazuje, która metoda nie działa. Uważam, że verb_noun (_modifier) ​​to dobry schemat nazewnictwa. Daje to nazwy takie jak Find_Customer_ByName zamiast Select_Customer_Using_NameIdx. Drugi przypadek może stać się niepoprawny po zmodyfikowaniu metody. W pierwszym przypadku możesz zamienić całą implementację bazy danych klienta.

BillThor
źródło
+1 za wzmiankę o wstawianiu, imo główny nacisk na wydajność i krótkie funkcje.
Garet Claborn,
4

Refaktoryzacja jednego wiersza kodu w funkcję wydaje się nadmierna. Mogą występować wyjątkowe przypadki, takie jak ver loooooong / comples wierszy lub wypraw, ale nie zrobiłbym tego, chyba że wiem, że funkcja będzie rosła w przyszłości.

i twój pierwszy przykład wskazuje na użycie globals (które mogą, ale nie muszą mówić o innych problemach w kodzie), przebuduję go jeszcze bardziej i uczynię te dwie zmienne jako parametry:

function conditionMet(x, condition){
   return x == condition;
}
....
conditionMet(1,(3-2));
conditionMet("abc","abc");

conditionMetPrzykład może być przydatne, jeśli stan jest długi i powtarzalny, takich jak:

function conditionMet(x, someObject){
   return x == ((someObject.valA + someObject.valB - 15.4) / /*...whole bunch of other stuff...*/);
}
FrustratedWithFormsDesigner
źródło
1
Jego pierwszy przykład nie wskazuje na globały. Jeśli już, to spójna metoda w bardzo spójnej klasie, w której większość zmiennych znajduje się na obiekcie.
Martin Blore,
7
Ta conditionMetfunkcja jest tylko pełnym ==operatorem. To prawdopodobnie nie jest przydatne.
1
Zdecydowanie nie używam globałów. @MeshMan, to jest dokładnie ten przykład z ... metody na klasie.
Mark Brown
1
@Mark Brown: @MeshMan: Ok, chyba przykład kodu był zbyt niejasny, by wiedzieć na pewno ...
FrustratedWithFormsDesigner
Pachnę conditionMet (x, relation condition) {tutaj, gdzie przekazujesz x, „==” i relację. Następnie nie musisz powtarzać kodu dla „<”, „>”, „<=”, „! =” Itd. Z drugiej strony - większość języków ma wbudowane te konstrukcje, ponieważ robią to operatory (warunek x ==). Funkcje instrukcji atomowych to o krok za daleko.
użytkownik nieznany
3

Rozważ to:

Prosta funkcja wykrywania kolizji:

bool collide(OBJ a, OBJ b)
{
    return(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) <= pow(a.radius + b.radius, 2));
}

Jeśli cały czas pisałeś ten „prosty” jeden linijka w swoim kodzie, możesz w końcu popełnić błąd. Poza tym pisanie tego w kółko byłoby bardzo torturujące.

muntoo
źródło
Jeśli mamy tutaj do czynienia z C, możemy po prostu nadużyć #define;)
Cole Johnson
Możesz nawet zamienić to na wolniejszą, ale odporną na przelewanie hipotekę, jeśli twoje rozmiary lub odległości staną się bardzo duże. Jest to oczywiście o wiele łatwiejsze do zrobienia raz.
Matt Krause
2

Nie, a to rzadko stanowi problem. Teraz, jeśli ktoś uważa, że ​​żadna funkcja nie powinna być dłuższa niż jeden wiersz kodu (choćby to mogło być tak proste), byłby to problem i pod pewnymi względami leniwy, ponieważ nie myśli o tym, co jest właściwe.

JeffO
źródło
Nie liczyłbym tego w liniach kodu, ale w wyrażeniach. „(x == warunek)” jest atomowy, więc umieściłbym go tylko w metodzie / funkcji, jeśli byłby użyty wiele razy ORAZ może zostać zmieniony, function isAdult () = { age >= 18; }ale na pewno nie isAtLeast18.
użytkownik nieznany
2

Powiedziałbym, że są za krótkie, ale to moja subiektywna opinia.

Dlatego:

  • Nie ma powodu, aby tworzyć funkcję, jeśli jest używana tylko raz lub dwa razy. Skaczę do defs ssać. Zwłaszcza z niezwykle szybkim kodem VS i C ++.
  • Przegląd klas. Kiedy masz tysiące małych funkcji, denerwuje mnie to. Cieszę się, gdy mogę przeglądać definicje klas i szybko zobaczyć, co to robi, a nie jak to SetXToOne, SetYToVector3, MultiplyNumbers, + 100 setter / getters.
  • W większości projektów pomocnicy stają się martwi po jednej lub dwóch fazach refaktoryzacji, a następnie wykonujesz „przeszukuj wszystko” -> usuń, aby pozbyć się przestarzałego kodu, zwykle około 25% + tego.

Długie funkcje są złe, ale funkcje krótsze niż 3 linie i wykonujące tylko jedną rzecz są równie złe IMHO.

Powiedziałbym więc, że napisz tylko małą funkcję, jeśli:

  • 3+ linie kodu
  • Czy rzeczy, których młodsi twórcy mogą przegapić (nie wiem)
  • Wykonuje dodatkową walidację
  • Jest używany lub użyje co najmniej 3x
  • Upraszcza często używany interfejs
  • Nie stanie się ciężarem podczas następnego refaktoryzacji
  • Ma jakieś specjalne znaczenie, na przykład specjalizacja szablonu lub coś takiego
  • Wykonuje niektóre odwołania do stałych izolacji, wpływa na zmienne parametry, pobiera elementy prywatne

Założę się, że następny programista (starszy) będzie miał lepsze rzeczy do roboty niż zapamiętanie wszystkich funkcji SetXToOne. Więc wkrótce zmienią się w ciężar własny.

Koder
źródło
1

Nie podoba mi się przykład nie. 1, ponieważ i-ta nazwa ogólna.

conditionMetnie wydaje się ogólny, więc oznacza konkretny warunek? Lubić

isAdult () = { 
  age >= 18 
}

To by było w porządku. To jest różnica semantyczna

isAtLeast18 () { age >= 18; } 

nie byłoby dla mnie w porządku.

Może jest często używany i może podlegać późniejszym zmianom:

getPreferredIcecream () { return List ("banana", "choclate", "vanilla", "walnut") }

też jest w porządku. Używając go wiele razy, wystarczy zmienić jedno miejsce, jeśli to konieczne - być może bitej śmietany stanie się jutro.

isXYZ (Foo foo) { foo.x > 15 && foo.y < foo.x * 2 }

nie jest atomowy i powinien dać ci dobrą okazję do przetestowania.

Jeśli potrzebujesz przekazać funkcję, oczywiście, przekaż co chcesz i napisz funkcje, które wyglądają inaczej.

Ale ogólnie widzę znacznie więcej funkcji, które są zbyt długie, niż funkcje, które są zbyt krótkie.

Ostatnie słowo: niektóre funkcje wyglądają tylko odpowiednio, ponieważ są napisane zbyt szczegółowe:

function lessThan (a, b) {
  if (a < b) return true else return false; 
}

Jeśli widzisz, że to to samo co

return (a < b); 

nie będziesz mieć problemu z

localLessThan = (a < b); 

zamiast

localLessThan = lessThan (a, b); 
nieznany użytkownik
źródło
0

Nie ma takiego kodu jak żaden kod!

Uprość to i nie komplikuj.

Nie jest leniwy, wykonuje swoją pracę!

RDL
źródło
0

Tak, dobrze jest mieć funkcję krótkiego kodu. W przypadku metod takich jak „getters”, „setters”, „accesors” jest bardzo powszechny, jak wspomniano w poprzednich odpowiedziach.

Czasami te krótkie funkcje „akcesorów” są wirtualne, ponieważ po zastąpieniu w podklasach funkcje będą miały więcej kodu.

Jeśli chcesz, żebyś nie działał tak krótko, no cóż, w wielu funkcjach, niezależnie od tego, czy są globalne czy metody, zwykle używam zmiennej „wynik” (styl pascal) zamiast bezpośredniego powrotu, co jest bardzo pomocne podczas korzystania z debuggera.

function int CalculateSomething() {
  int Result = -1;

   // more code, maybe, maybe not

  return Result;
}
umlcat
źródło
0

Zbyt krótki nigdy nie stanowi problemu. Oto kilka przyczyn krótkich funkcji:

Wielokrotnego użytku

Np. Jeśli masz funkcję, taką jak metoda set, możesz ją potwierdzić, aby upewnić się, że parametry są prawidłowe. Sprawdzanie to należy wykonywać wszędzie, gdzie ustawiona jest zmienna.

Konserwowalność

Możesz użyć oświadczenia, które Twoim zdaniem w przyszłości może się zmienić. Na przykład teraz wyświetlasz symbol w kolumnie, ale później może on zmienić się w coś innego (lub nawet sygnał dźwiękowy).

Jednolitość

Używasz np. Wzoru elewacji i jedyne, co robi funkcja, to dokładne przekazywanie funkcji do innej bez zmiany argumentów.

Michel Keijzers
źródło
0

Gdy nadasz sekcji kodu nazwę, jest ona zasadniczo przeznaczona do nadania jej nazwy . Może to wynikać z kilku powodów, ale najważniejsze jest, aby nadać mu sensowną nazwę, która dodaje do twojego programu. Nazwy takie jak „addOneToCounter” nic by nie dodały, ale conditionMet()by to zrobiły .

Jeśli potrzebujesz reguły, aby dowiedzieć się, czy fragment jest za krótki, czy za długi, zastanów się, ile czasu zajmuje znalezienie sensownej nazwy fragmentu. Jeśli nie możesz w rozsądnym czasie, fragment kodu nie ma odpowiedniego rozmiaru.

użytkownik1249
źródło
0

Nie, ale może być zbyt zwięzłe.

Pamiętaj: kod jest zapisywany raz, ale czytany wiele razy.

Nie pisz kodu dla kompilatora. Napisz to dla przyszłych programistów, którzy będą musieli utrzymywać Twój kod.

Jim G.
źródło
-1

Tak, kochanie, funkcja może być coraz mniejsza, a to, czy jest dobre czy złe, zależy od używanego języka / frameworka.

Moim zdaniem pracuję głównie nad technologiami Front End, małe funkcje są przeważnie używane jako funkcje pomocnicze, które z pewnością będziesz ich używać podczas pracy z małymi filtrami i przy użyciu tej samej logiki w całej aplikacji. Jeśli twoja aplikacja ma zbyt dużo wspólnej logiki, będzie mnóstwo małych funkcji.

Ale w aplikacji, w której nie masz wspólnej logiki, nie będziesz musiał tworzyć małych funkcji, ale możesz podzielić kod na segmenty, w których zarządzanie i zrozumienie staje się łatwe.

Ogólnie rzecz biorąc, rozbicie ogromnego kodu na małą funkcję to bardzo dobre podejście. W nowoczesnych ramach i językach będziesz musiał to zrobić np

data => initScroll(data)

jest anonimową funkcją w JavaScript 2017 i maszynopisie ES 2017

getMarketSegments() {
 this.marketService.getAllSegments(this.project.id)
  .subscribe(data => this.segments = data, error => console.log(error.toString()));
}

w powyższym kodzie widać 3 deklarację funkcji i 2 wywołania funkcji, jest to proste zgłoszenie serwisowe w Angular 4 z maszynowym skryptem. Możesz myśleć o tym jak o swoich wymaganiach

([] 0)
([x] 1)
([x y] 2)

Powyżej są 3 anonimowe funkcje w języku clojure

(def hello (fn [] "Hello world"))

Powyższa jest deklaracją funkcjonalną w zamknięciu

Więc tak, FUNKCJE mogą być mniejsze, ale czy są dobre czy złe, jeśli masz takie funkcje jak:

incrementNumber(numb) { return ++numb; }

Cóż, nie jest to dobrą praktyką, ale co zrobić, jeśli używasz tej funkcji w znaczniku HTML, tak jak robimy to w Angular Framework, jeśli nie było obsługi Increment lub Decrement w Angularowych szablonach HTML, to byłoby to rozwiązanie dla mnie.

Weźmy inny przykład

insertInArray(array, newKey) {
 if (!array.includes(newKey)) {
   array.push(newKey);
 }
}

Powyższy przykład jest koniecznością podczas gry, gdy tablice wewnątrz kątowych szablonów HTML. Czasami będziesz musiał stworzyć małe funkcje

Anwaar Ulhaq
źródło
-2

Nie zgadzam się z prawie odpowiedzią udzieloną w tym czasie.

Niedawno znalazłem kolegę, który pisze wszystkich członków klas w następujący sposób:

 void setProperty(int value){ mValue=value; }
 int getProperty() const { return (mValue); }

Może to być dobry kod w sporadycznych przypadkach, ale na pewno nie, jeśli zdefiniujesz wiele klas mających wiele właściwości. Nie chcę przez to powiedzieć, że metody takie jak powyższe nie będą pisane. Ale jeśli skończysz pisać większość kodu jako „opakowanie” prawdziwej logiki, coś jest nie tak.

Prawdopodobnie programista nie rozumie ogólnej logiki, obawia się przyszłych zmian i refaktoryzacji kodu.

Definicja interfejsu wynika z rzeczywistej potrzeby; w rzeczy samej, jeśli musisz ustawić i uzyskać prostą właściwość (bez dużej logiki, co czyni członka krótszym), będzie to możliwe. Ale jeśli prawdziwą potrzebę można przeanalizować w inny sposób, dlaczego nie zdefiniować bardziej intuicyjnego interfejsu?

Aby naprawdę odpowiedzieć na pytanie, oczywiście, że nie, a powód jest bardzo oczywisty: należy określić metodę / właściwość / whatelse dla tego, czego potrzebuje. Nawet pusta funkcja wirtualna ma powód, aby istnieć.

Luca
źródło
6
Istnieje dobry powód, aby dodać metody akcesor / mutator, o których wspominasz w „strachu przed przyszłym ... refaktoryzacją”. Ale masz również rację, że nie dodaje wartości do kodu. Ani przez zmniejszenie rozmiaru (lokalnie lub globalnie), ani przez zwiększenie czytelności. Moim zdaniem, przemawia to za słabym projektowaniem języka w konwencji Java i JavaBeans. Jest to jeden z wielu obszarów, w którym język C # z powodzeniem poprawił język, aby rozwiązać oba problemy związane z automatyczną składnią właściwości . Jako programista Java zgadzam się z twoim surowym stylem dla podstawowych klas strukturalnych.
Anm
1
Funkcje getter / setter są dobre, nawet jeśli wszystko, co teraz robią, to odczyt lub zapis zmiennej. W praktyce możesz sobie wyobrazić scenariusz, w którym później zdecydujesz się drukować wartość na ekranie za każdym razem, gdy zmienia się pole, lub sprawdzać, czy ta wartość jest poprawna, czy nie. (Może jest to mianownik dla ułamka i chcesz sprawdzić, czy nie jest to zero).
Rei Miyasaka
2
osoby pobierające i ustawiające są okropne, pomimo ich prawidłowego użytkowania. Jeśli konieczne jest ich użycie, uważam to za błąd języka.
Carson Myers,
@Rei: dlaczego klasa zewnętrzna musiałaby sprawdzać wartość mianowników? Powinno to zostać wykonane przez funkcję divide () klasy Fraction lub klasa Fraction powinna udostępnić funkcję isDivisible () w celu sprawdzenia zerowych mianowników. Potrzebujący pobierających / ustawiających są zwykle zapachem kodu, który charakteryzuje się wysokim stopniem sprzężenia i / lub niską kohezją (tj. Złą).
Lie Ryan
@ Lie Co? Nigdy nie mówiłem, że do sprawdzania mianowników powinna być używana klasa zewnętrzna. Mówię, że klasa zewnętrzna może przez przypadek ustawić mianownik na 0. Celem przeprowadzania kontroli poprawności u seterów jest zachęcenie do wcześniejszych błędów w kodzie konsumenta. Zapewnienie funkcji isDivisible (), która może zostać wywołana lub nie, również nie działa w tym celu. A w jaki sposób potrzeba pobierania / ustawiania wskazuje, że występuje wysokie sprzężenie?
Rei Miyasaka,