Zawsze zastanawiałem się, co myślą inni programiści o idei tworzenia czystych funkcji estetycznych.
Że mam funkcję, która przetwarza porcji danych: Function ProcessBigData
. Muszę powiedzieć kilka etapów procesu, dotyczy tylko tych danych: Step1
, Step2
, Step3
.
Normalnym podejściem, które najbardziej widzę w kodzie źródłowym, jest pisanie komentarzy w następujący sposób:
Function ProcessBigData:
# Does Step1
Step1..
Step1..
#Does Step2
Step2..
Step2..
To, co zwykle robię, ale zawsze czułem się źle z powodu braku takiego stylu kodowania przez innych kolegów to:
Function ProcessBigData:
Function Step1:
Step1..
Step1..
Function Step2:
Step2..
Step2..
Step1() -> Step2()
Niepokoi mnie przede wszystkim ewentualne wady takiego stylu w Javascript i Python
Czy są jakieś alternatywy, których nie widzę?
javascript
python
coding-style
Slytael
źródło
źródło
Odpowiedzi:
To nie jest tak dziwne, jak mogłoby się wydawać. Na przykład w Standard ML zwykle ogranicza się zakres funkcji pomocniczych. To prawda, że SML ma składnię, aby to ułatwić:
Rozważałbym ten dobry styl, biorąc pod uwagę, że 1) małe funkcje ułatwiają rozumowanie na temat programu i 2) sygnalizuje czytelnikowi, że te funkcje nie są używane poza tym zakresem.
Przypuszczam, że jest możliwe, że tworzenie funkcji wewnętrznych jest pewne, gdy wywoływana jest funkcja zewnętrzna (nie wiem, czy JS lub Python ją optymalizują), ale wiesz, co mówią o przedwczesnej optymalizacji.
źródło
Zazwyczaj dobrze jest to robić, gdy tylko jest to możliwe, ale lubię myśleć o tego rodzaju pracy nie jako o „krokach”, ale o podzadaniach .
Podzadanie jest określoną jednostką pracy, którą można wykonać: ma określoną odpowiedzialność oraz zdefiniowane dane wejściowe i dane wyjściowe (pomyśl o „S” w SOLID ). Podzadanie nie musi nadawać się do ponownego użycia: niektórzy ludzie myślą: „Nigdy nie będę musiał dzwonić do tego z niczego innego, więc po co pisać to jako funkcję?” ale to błąd.
Spróbuję również przedstawić korzyści i sposób, w jaki dotyczy to funkcji zagnieżdżonych (zamknięć) w porównaniu z inną funkcją w klasie. Ogólnie rzecz biorąc, zalecałbym, aby nie używać zamknięć, chyba że są one szczególnie potrzebne (istnieje wiele zastosowań, ale dzielenie kodu na logiczne części nie jest jednym z nich).
Czytelność.
Ponad 200 wierszy kodu proceduralnego (treści funkcji) jest trudnych do odczytania. 2-20 funkcji linii jest łatwych do odczytania. Kod jest dla ludzi.
Zagnieżdżone czy nie, przeważnie zyskujesz na czytelności, chyba że używasz wielu zmiennych z zakresu nadrzędnego, w którym to przypadku odczytanie może być równie trudne.
Ogranicz zakres zmiennej
Posiadanie innej funkcji zmusza cię do ograniczenia zakresu zmiennej, a konkretnie przekazania tego, czego potrzebujesz.
Często powoduje to również lepszą strukturę kodu, ponieważ jeśli potrzebujesz jakiejś zmiennej stanu z wcześniejszego „kroku”, może się okazać, że istnieje inna podzadanie, które powinno zostać napisane i wykonane jako pierwsze, aby uzyskać tę wartość. Innymi słowy, utrudnia to pisanie wysoce sprzężonych fragmentów kodu.
Posiadanie funkcji zagnieżdżonych umożliwia dostęp do zmiennych w zakresie nadrzędnym z wnętrza funkcji zagnieżdżonej (zamknięcie). Może to być bardzo przydatne, ale może również prowadzić do subtelnych, trudnych do znalezienia błędów, ponieważ wykonanie funkcji może nie nastąpić w sposób, w jaki został napisany. Jest tak nawet w przypadku, gdy modyfikujesz zmienne w zakresie nadrzędnym (ogólnie bardzo zły pomysł).
Testy jednostkowe
Każda podzadanie, zaimplementowana funkcja (lub nawet klasa) jest samodzielnym, testowalnym fragmentem kodu. Korzyści z testów jednostkowych i TDD są dobrze udokumentowane gdzie indziej.
Korzystanie z zagnieżdżonych funkcji / zamknięć nie pozwala na testowanie jednostkowe. Dla mnie jest to przełom i powód, dla którego powinieneś po prostu inną funkcję, chyba że istnieje szczególna potrzeba zamknięcia.
Praca w zespole / projekt z góry na dół
Podzadania mogą być pisane przez różne osoby, niezależnie, w razie potrzeby.
Nawet samemu przydaje się przy pisaniu kodu, aby po prostu wywołać podzadanie, które jeszcze nie istnieje, jednocześnie budując główną funkcjonalność, i martwić się o faktyczne wdrożenie podzadania tylko wtedy, gdy wiesz, że uzyska on potrzebne wyniki w znaczący sposób. Jest to również nazywane odgórnym projektowaniem / programowaniem.
Ponowne użycie kodu
Okej, więc pomimo tego, co powiedziałem wcześniej, czasem tak naprawdę jest powód do ponownego użycia podzadania do czegoś innego. W ogóle nie jestem zwolennikiem „architektury astronautów”, ale po prostu pisząc luźno powiązany kod, możesz później skorzystać z ponownego użycia.
Często ponowne użycie oznacza pewne refaktoryzowanie, co jest całkowicie oczekiwane, ale refaktoryzacja parametrów wejściowych do małej samodzielnej funkcji jest DUŻO łatwiejsza niż wyodrębnienie jej z ponad 200 funkcji liniowych kilka miesięcy po jej napisaniu, co jest naprawdę moim celem tutaj.
Jeśli użyjesz funkcji zagnieżdżonej, ponowne użycie jej jest generalnie kwestią refaktoryzacji do oddzielnej funkcji, co znowu jest powodem, dla którego twierdzę, że zagnieżdżenie nie jest właściwą drogą.
źródło