Mam klasę, która ustawia tablicę węzłów i łączy je ze sobą w strukturze graficznej. Czy najlepiej:
- Zachowaj funkcjonalność, aby zainicjować i połączyć węzły w jednej funkcji
- Mają funkcje inicjalizacji i połączenia w dwóch różnych funkcjach (i mają zależną kolejność, w której funkcje muszą być wywoływane - należy jednak pamiętać, że te funkcje są prywatne).
Metoda 1: (Zła w tym, że jedna funkcja robi dwie rzeczy, ALE utrzymuje zgrupowane funkcje zależne razem - nigdy nie należy łączyć węzłów bez inicjalizacji).
init() {
setupNodes()
}
private func setupNodes() {
// 1. Create array of nodes
// 2. Go through array, connecting each node to its neighbors
// according to some predefined constants
}
Metoda 2: (Lepiej w tym sensie, że jest to samo-dokumentowanie, ALE connectNodes () nigdy nie powinno być wywoływane przed setupNodes (), więc każdy, kto pracuje z wewnętrznymi elementami klasy, musi wiedzieć o tej kolejności).
init() {
setupNodes()
}
private func setupNodes() {
createNodes()
connectNodes()
}
private func createNodes() {
// 1. Create array of nodes
}
private func connectNodes() {
// 2. Go through array, connecting each node to its neighbors
// according to some predefined constants
}
Podekscytowany, aby usłyszeć jakieś myśli.
Odpowiedzi:
Problem, z którym masz do czynienia, nazywa się sprzężeniem czasowym
Masz rację, jeśli chodzi o zrozumiałość tego kodu:
Mogę zgadnąć, co się tam dzieje, ale powiedz mi, czy to sprawia, że to, co się dzieje, jest trochę jaśniejsze:
Ma to tę dodatkową zaletę, że jest mniej sprzężone z modyfikowaniem zmiennych instancji, ale dla mnie czytelność jest numerem jeden.
To sprawia
connectNodes()
, że zależność od węzłów jest jawna.źródło
Oddzielne funkcje z dwóch powodów:
1. Funkcje prywatne są prywatne dla dokładnie takiej sytuacji.Twoja
init
funkcja jest publiczna, a jej interfejs, zachowanie i wartość zwracana są tym, czego musisz się martwić o ochronę i zmianę. Wynik, którego oczekujesz od tej metody, będzie taki sam, bez względu na to, jakiej implementacji używasz.Ponieważ reszta funkcji jest ukryta za tym prywatnym słowem kluczowym, można go wdrożyć, jak chcesz ... więc równie dobrze możesz uczynić go ładnym i modułowym, nawet jeśli jeden bit zależy od drugiego wywołania pierwszego.
2. Łączenie węzłów może nie być funkcją prywatnąCo jeśli w pewnym momencie chcesz dodać inne węzły do tablicy? Czy niszczysz istniejącą konfigurację i całkowicie ją ponownie inicjujesz? A może dodajesz węzły do istniejącej tablicy, a następnie uruchamiasz
connectNodes
ponownie?Być
connectNodes
może może mieć rozsądną odpowiedź, jeśli tablica węzłów nie została jeszcze utworzona (wyrzuć wyjątek? Zwróć pusty zestaw? Musisz zdecydować, co ma sens w twojej sytuacji).źródło
Może się również okazać (w zależności od stopnia złożoności każdego z tych zadań), że jest to dobry szew do podzielenia innej klasy.
(Nie jestem pewien, czy Swift działa w ten sposób, ale pseudo-kod :)
To rozdziela obowiązki związane z tworzeniem i modyfikowaniem węzłów w oddzielnych klasach:
NodeGenerator
zależy tylko na tworzeniu / pobieraniu węzłów, aYourClass
tylko na podłączaniu węzłów.źródło
Oprócz tego, że jest to dokładny cel prywatnych metod, Swift daje możliwość korzystania z funkcji wewnętrznych.
Metody wewnętrzne są idealne dla funkcji, które mają tylko jedną witrynę wywołań, ale wydaje się, że nie uzasadniają oddzielnych funkcji prywatnych.
Na przykład bardzo często występuje publiczna rekurencyjna funkcja „wejścia”, która sprawdza warunki wstępne, ustawia niektóre parametry i deleguje do prywatnej funkcji rekurencyjnej, która wykonuje tę pracę.
Oto przykład, jak może to wyglądać w tym przypadku:
Zwróć uwagę na to, jak używam zwracanych wartości i parametrów do przekazywania danych, zamiast mutowania stanu wspólnego. To sprawia, że przepływ danych jest znacznie bardziej oczywisty na pierwszy rzut oka, bez potrzeby przeskakiwania do implementacji.
źródło
Każda deklarowana funkcja niesie za sobą ciężar dodawania dokumentacji i generalizowania jej, tak aby można ją było wykorzystać w innych częściach programu. Niesie również ciężar zrozumienia, jak inne funkcje w pliku mogą go używać dla kogoś, kto czyta kod.
Jeśli jednak nie jest używany przez inne części twojego programu, nie ujawniłbym go jako osobnej funkcji.
Jeśli twój język to obsługuje, nadal możesz mieć funkcję jedna funkcja robi jedną rzecz, używając funkcji zagnieżdżonych
Miejsce deklaracji ma bardzo duże znaczenie i w powyższym przykładzie jest jasne, bez potrzeby dalszych wskazówek, że funkcje wewnętrzne mają być używane tylko w ciele funkcji zewnętrznej.
Nawet jeśli deklarujesz je jako funkcje prywatne, zakładam, że są one nadal widoczne dla całego pliku. Musisz więc zadeklarować je blisko deklaracji funkcji głównej i dodać dokumentację, która wyjaśnia, że mają być używane tylko przez funkcję zewnętrzną.
Nie sądzę, że musicie robić dokładnie jedno lub drugie. Najlepsze rzeczy do zrobienia różnią się w zależności od przypadku.
Podzielenie go na wiele funkcji z pewnością zwiększa ogólne zrozumienie, dlaczego istnieją 3 funkcje i jak one wszystkie działają ze sobą, ale jeśli logika jest złożona, to ten dodatkowy narzut może być znacznie mniejszy niż prostota wprowadzona przez rozbicie złożonej logiki na prostsze części.
źródło
private
zezwala na dostęp tylko w obrębie typu załączającego (struct / class / enum), natomiastfileprivate
umożliwia dostęp do całego pliku