Czy jest jakiś cel deklarowania init()
metody dla typu?
Nie pytam, czy powinniśmy preferować init()
konstruktor, czy jak uniknąć deklarowaniainit()
.
Pytam, czy istnieje jakikolwiek uzasadnienie deklaracji init()
metody (zobaczenie, jak często jest stosowana) lub czy jest to zapach kodu i należy tego unikać.
Ten init()
idiom jest dość powszechny, ale jeszcze nie widziałem żadnych realnych korzyści.
Mówię o typach, które zachęcają do inicjalizacji za pomocą metody:
class Demo {
public void init() {
//...
}
}
Kiedy będzie to kiedykolwiek przydatne w kodzie produkcyjnym?
Wydaje mi się, że może to być zapach kodu, ponieważ sugeruje, że konstruktor nie w pełni zainicjował obiekt, w wyniku czego powstał częściowo obiekt. Obiekt nie powinien istnieć, jeśli jego stan nie jest ustawiony.
To sprawia, że wierzę, że może to być część techniki stosowanej do przyspieszenia produkcji w sensie aplikacji korporacyjnych. To jedyny logiczny powód, dla którego mogę wymyślić taki idiom, po prostu nie jestem pewien, czy byłoby to korzystne, gdyby tak było.
init()
pochodną, czy odwrotnie?) Jeśli tak, to przykład pozwalający klasie podstawowej wykonać „postkonstruktor” ”, które można wykonać tylko po zakończeniu budowy najbardziej pochodnej klasy. Jest to przykład inicjalizacji wielofazowej.Odpowiedzi:
Tak, to zapach kodu. Zapach kodu nie jest czymś, co zawsze musi zostać usunięte. To coś, co sprawia, że patrzysz po raz drugi.
Tutaj masz obiekt w dwóch zasadniczo różnych stanach: przedinicjalizacyjnym i poinicjalizacyjnym. Państwa te mają różne obowiązki, różne metody, które mogą być wywoływane, i różne zachowania. W rzeczywistości są to dwie różne klasy.
Jeśli fizycznie utworzysz z nich dwie osobne klasy, to statycznie usuniesz całą klasę potencjalnych błędów, kosztem być może sprawiając, że twój model nie będzie pasował do „prawdziwego modelu świata” tak blisko. Zazwyczaj nazwać pierwszy
Config
lubSetup
czy coś takiego.Więc następnym razem spróbuj przekształcić idiomy konstruktywno-inicjujące w modele dwuklasowe i zobacz, jak ci się to uda.
źródło
this
konstruktora jest zapachem kodu i może powodować kod podatny na błędy, i unikanie go jest zalecane bez względu na domenę, do której należy Twój projekt).To zależy.
init
Metoda zapach kodu gdy nie jest to konieczne, aby inicjowanie przedmiot oddzielony od konstruktora. Czasami zdarza się, że warto oddzielić te kroki.Szybkie wyszukiwanie w Google dało mi ten przykład. Mogę łatwo wyobrazić sobie więcej przypadków, w których kod wykonywany podczas przydzielania obiektów (konstruktor) mógłby być lepiej oddzielony od samej inicjalizacji. Być może masz system zrównany, a alokacja / konstrukcja odbywa się na poziomie X, ale inicjalizacja tylko na poziomie Y, ponieważ tylko Y może zapewnić niezbędne parametry. Być może „init” jest kosztowny i musi zostać uruchomiony tylko dla podzbioru przydzielonych obiektów, a określenie tego podzbioru można wykonać tylko na poziomie Y. Lub chcesz zastąpić (wirtualną) metodę „init” w pochodnej klasa, której nie można wykonać za pomocą konstruktora. Być może poziom X zapewnia przydzielone obiekty z drzewa dziedziczenia, ale poziom Y nie jest świadomy konkretnej pochodnej, tylko wspólnego interfejsu (gdzie
init
może zdefiniowane).Oczywiście, z mojego doświadczenia
init
wynika, że przypadki te stanowią jedynie niewielki procent standardowego przypadku, w którym wszystkie inicjowanie można wykonać bezpośrednio w konstruktorze, a ilekroć zobaczysz oddzielną metodę, dobrym pomysłem może być zakwestionowanie jej konieczności.źródło
init
metody. Jednak gdy tylko zobaczysz taką metodę, możesz zakwestionować jej konieczność.init()
metoda byłaby właściwie wykorzystywana , jestem pewien, że skorzystałbym z poznania jej celu. Przepraszam za moją niewiedzę, jestem po prostu zaskoczony, jak trudno mi znaleźć dla niej pożytek, uniemożliwiając mi rozważenie tego, czego należy unikaćMoje doświadczenie dzieli się na dwie grupy:
Z mojego osobistego doświadczenia widziałem tylko kilka przykładów (1), ale wiele innych przykładów (2). W związku z tym zwykle zakładam, że init () to zapach kodu, ale nie zawsze tak jest. Czasami po prostu nie da się tego obejść.
Odkryłem, że użycie Wzorca konstruktora często pomaga usunąć potrzebę / chęć posiadania init ().
źródło
init()
metody go rozwiązałoby? Tainit()
metoda wymagałaby parametrów do zaakceptowania zależności lub musiałaby zostać utworzona instancja zależności w ramachinit()
metody, co można również zrobić z konstruktorem. Czy możesz podać przykład?Typowy scenariusz, w którym przydaje się metoda Init, ma plik konfiguracyjny, który chcesz zmienić, i uwzględnić tę zmianę bez ponownego uruchamiania aplikacji. To oczywiście nie oznacza, że metodę Init należy wywoływać niezależnie od konstruktora. Możesz wywołać metodę Init z konstruktora, a następnie wywołać ją później, gdy / jeśli zmienią się parametry konfiguracyjne.
Podsumowując: tak jak w przypadku większości dylematów, czy jest to zapach kodu, czy nie, zależy od sytuacji i okoliczności.
źródło
Config
?update
/reload
prawdopodobnie byłby bardziej opisowy dla tego rodzaju zachowania), aby faktycznie zarejestrować te zmiany . W takim przypadku to powiadomienie spowoduje wewnętrzną zmianę wartości konfiguracji w aplikacji, co, jak sądzę, można zaobserwować, obserwując konfigurację, powiadamiając obserwatorów, kiedy konfiguracja ma zmienić jedną z jej wartości. A może źle rozumiem twój przykład?Zależy od tego, jak z nich korzystasz.
Używam tego wzorca w językach odśmiecanych, takich jak Java / C #, gdy nie chcę ciągle przenosić obiektu na stos (np. Kiedy robię grę wideo i muszę utrzymać wysoką wydajność, śmieciarze zabijają wydajność). Używam konstruktora do dokonywania innych przydziałów sterty, których potrzebuje, oraz
init
do tworzenia podstawowego przydatnego stanu tuż za każdym razem, gdy chcę go ponownie użyć. Jest to związane z koncepcją pul obiektów.Jest to również przydatne, jeśli masz kilka konstruktorów, które mają wspólny podzbiór instrukcji inicjalizacji, ale w takim przypadku
init
będą prywatne. W ten sposób mogę zminimalizować każdy konstruktor tak bardzo, jak to możliwe, więc każdy zawiera tylko swoje unikalne instrukcje i pojedyncze wywołanieinit
do wykonania reszty.Ogólnie jest to jednak zapach kodu.
źródło
reset()
metoda nie byłaby bardziej opisowa dla twojego pierwszego stwierdzenia? Jeśli chodzi o drugi (wiele konstruktorów), posiadanie wielu konstruktorów to zapach kodu. Zakłada, że obiekt ma wiele celów / obowiązków, co sugeruje naruszenie zasad SRP. Obiekt powinien mieć jedną odpowiedzialność, a konstruktor powinien zdefiniować wymagane zależności dla tej jednej odpowiedzialności. Jeśli masz wiele konstruktorów z powodu wartości opcjonalnych, powinni oni teleskopować (który jest również zapachem kodu, zamiast tego należy użyć konstruktora).string
listę konstruktorów dowolnego języka , mnóstwo opcji. Dla mnie zwykle jest to maksymalnie 3 konstruktorów, ale wspólny podzbiór instrukcji podczas inicjalizacji ma sens, gdy współużytkują dowolny kod, ale różnią się w jakikolwiek sposób.String
, można to rozwiązać, oddzielając tworzenie ciągów. W końcu aString
jest aString
, a jego konstruktor powinien akceptować tylko to, co jest potrzebne, aby działało zgodnie z potrzebami. Większość z tych konstruktorów jest narażona na cele konwersji, co jest niewłaściwym użyciem konstruktorów. Konstruktory nie powinny wykonywać logiki lub ryzykują nieudaną inicjalizację, pozostawiając bezużyteczny obiekt.init()
metody mogą mieć pewien sens, gdy masz obiekty, które wymagają zasobów zewnętrznych (takich jak na przykład połączenie sieciowe), które są jednocześnie używane przez inne obiekty. Być może nie chcesz / nie musisz zawieszać zasobu przez cały okres istnienia obiektu. W takich sytuacjach możesz nie chcieć alokować zasobu w konstruktorze, gdy alokacja zasobów może się nie powieść.Zwłaszcza w programowaniu wbudowanym chcesz mieć deterministyczny ślad pamięci, więc powszechną (dobrą?) Praktyką jest wczesne wywoływanie konstruktorów, może nawet statyczne, i inicjowanie dopiero później, gdy zostaną spełnione określone warunki.
Poza tymi przypadkami myślę, że wszystko powinno przejść do konstruktora.
źródło
init
metody są zbyt ograniczające, IMHO.Ogólnie wolę konstruktor, który odbiera wszystkie argumenty wymagane dla instancji funkcjonalnej. To wyjaśnia wszystkie zależności tego obiektu.
Z drugiej strony używam prostego frameworku konfiguracyjnego, który wymaga konstruktora publicznego bez parametrów i interfejsów do wstrzykiwania zależności i wartości konfiguracyjnych. Po tym, struktura konfiguracji wywołuje
init
metodę obiektu: teraz otrzymałeś wszystkie rzeczy, które mam dla ciebie, wykonaj ostatnie kroki, aby przygotować się do pracy. Ale uwaga: to struktura konfiguracji, która automatycznie wywołuje metodę init, więc nie zapomnisz jej wywołać.źródło
Nie ma zapachu kodu, jeśli metoda init () jest semantycznie osadzona w cyklu życia obiektu.
Jeśli potrzebujesz wywołać init (), aby ustawić obiekt w spójnym stanie, jest to zapach kodu.
Istnieje kilka technicznych przyczyn istnienia takiej struktury:
źródło
Nazwa init może czasami być nieprzejrzysta. Weźmy samochód i silnik. Aby uruchomić samochód (tylko włączyć zasilanie, aby słuchać radia), chcesz sprawdzić, czy wszystkie systemy są gotowe do pracy.
Więc budujesz silnik, drzwi, koło itp. Twój ekran pokazuje silnik = wyłączony.
Nie ma potrzeby rozpoczynania monitorowania silnika itp., Ponieważ wszystkie są drogie. Następnie, gdy przekręcisz kluczyk w celu zapłonu, wołasz silnik-> start. Zaczyna działać wszystkie drogie procesy.
Teraz widzisz silnik = włączony. I rozpoczyna się proces zapłonu.
Samochód nie uruchomi się bez dostępnego silnika.
Możesz zastąpić silnik złożonymi obliczeniami. Jak komórka Excela. Nie wszystkie komórki muszą być aktywne przez cały czas ze wszystkimi modułami obsługi zdarzeń. Kiedy skupisz się na komórce, możesz ją uruchomić. W ten sposób zwiększa się wydajność.
źródło