Powiedzmy, że z jakiegoś powodu wszystkie obiekty są tworzone w ten sposób $ obj = CLASS :: getInstance (). Następnie wstrzykujemy zależności za pomocą ustawiaczy i przeprowadzamy inicjalizację początkową za pomocą $ obj-> initInstance (); Czy są jakieś realne problemy lub sytuacje, których nie można rozwiązać, jeśli w ogóle nie będziemy używać konstruktorów?
Ps powodem dla utworzenia obiektu w ten sposób jest to, że możemy zastąpić klasę wewnątrz getInstance () zgodnie z pewnymi regułami.
Pracuję w PHP, jeśli to ważne
object-oriented-design
class-design
Axel Foley
źródło
źródło
Odpowiedzi:
Powiedziałbym, że to poważnie utrudnia twoją przestrzeń projektową.
Konstruktory są doskonałym miejscem do inicjowania i sprawdzania przekazywanych parametrów. Jeśli nie możesz ich już do tego używać, inicjalizacja, obsługa stanu (lub oczywiste odrzucanie konstruktora „uszkodzonych” obiektów) staje się o wiele trudniejsze i częściowo niemożliwe.
Na przykład, jeśli każdy
Foo
obiekt potrzebuje,Frobnicator
to może sprawdzić w swoim konstruktorze, czy wartośćFrobnicator
jest różna od zera. Jeśli usuniesz konstruktora, trudniej będzie to sprawdzić. Czy sprawdzasz w każdym punkcie, w którym byłby używany? Winit()
metodzie (skutecznie eksternalizującej metodę konstruktora)? Nigdy tego nie sprawdzasz i masz nadzieję na najlepsze?Podczas mógłby prawdopodobnie nadal realizować wszystko (mimo wszystko, wciąż jesteś turing kompletne), niektóre rzeczy będzie dużo trudniej zrobić.
Osobiście sugerowałbym przyjrzenie się wstrzykiwaniu zależności / inwersji kontroli . Techniki te umożliwiają także przełączanie konkretnych klas implementacyjnych, ale nie uniemożliwiają pisania / używania konstruktorów.
źródło
HttpClient
, sprawdza, czy parametr ten nie ma wartości null). A jeśli te ograniczenia nie zostaną spełnione, może to spowodować wyjątek. Nie jest to tak naprawdę możliwe w przypadku podejścia konstruowania i ustawiania wartości.init()
, co jest całkowicie możliwe - chociaż to tylko nakłada większe obciążenie związane z utrzymaniem.2 zalety dla konstruktorów:
Konstruktory umożliwiają atomowe wykonanie kroków konstrukcyjnych obiektu.
Mógłbym uniknąć konstruktora i do wszystkiego użyć seterów, ale co z obowiązkowymi właściwościami, jak sugerował Joachim Sauer? W przypadku konstruktora obiekt posiada własną logikę konstrukcyjną, aby upewnić się, że nie ma niepoprawnych instancji takiej klasy .
Jeśli utworzenie instancji
Foo
wymaga ustawienia 3 właściwości, konstruktor może odwołać się do wszystkich 3, zweryfikować je i zgłosić wyjątek, jeśli są one nieprawidłowe.Kapsułkowanie
Opierając się wyłącznie na ustawiaczach, ciężar spoczywający na użytkowniku obiektu spoczywa na jego prawidłowym zbudowaniu. Mogą istnieć różne kombinacje właściwości, które są prawidłowe.
Na przykład każde
Foo
wystąpienie wymaga wystąpieniaBar
jako właściwościbar
lub wystąpieniaBarFinder
jako właściwościbarFinder
. Może używać jednego z nich. Możesz utworzyć konstruktor dla każdego poprawnego zestawu parametrów i w ten sposób egzekwować konwencje.Logika i semantyka obiektów żyją w samym obiekcie. To dobre kapsułkowanie.
źródło
Tak, możesz żyć bez konstruktorów.
Pewnie, możesz skończyć z dużą ilością zduplikowanego kodu płyty kotła. A jeśli twoja aplikacja ma jakąkolwiek skalę, prawdopodobnie spędzisz dużo czasu próbując zlokalizować źródło problemu, gdy ten kod bojlera nie jest konsekwentnie używany w całej aplikacji.
Ale nie, nie potrzebujesz „własnych” konstruktorów. Oczywiście nie potrzebujesz też ściśle klas i obiektów.
Teraz, jeśli Twoim celem jest użycie jakiegoś fabrycznego wzorca do tworzenia obiektów, nie wyklucza to wzajemnie korzystania z konstruktorów podczas inicjowania obiektów.
źródło
Zaletą używania konstruktorów jest to, że ułatwiają one zapewnienie, że nigdy nie będziesz mieć nieprawidłowego obiektu.
Konstruktor daje możliwość ustawienia wszystkich zmiennych składowych obiektu na prawidłowy stan. Gdy upewnisz się, że żadna metoda mutatora nie może zmienić obiektu w nieprawidłowy stan, nigdy nie będziesz mieć nieprawidłowego obiektu, co uratuje cię od wielu błędów.
Ale gdy nowy obiekt jest tworzony w niepoprawnym stanie i musisz wywołać niektóre obiekty ustawiające, aby wprowadzić go w prawidłowy stan, w którym można go użyć, ryzykujesz, że konsument klasy zapomni o wywołaniu tych ustawień lub wywoła je niepoprawnie i kończy się to nieprawidłowym obiektem.
Obejściem może być tworzenie obiektów tylko metodą fabryczną, która sprawdza poprawność każdego tworzonego obiektu przed zwróceniem go do obiektu wywołującego.
źródło
Myślę, że utrudniasz to, niż musi być. Możemy dobrze wstrzykiwać zależności za pomocą konstruktora - a jeśli masz ich dużo, po prostu użyj struktury przypominającej słownik, aby określić, których chcesz użyć:
Wewnątrz konstruktora możesz zapewnić spójność w następujący sposób:
$args
może być następnie użyty do ustawienia prywatnych członków w razie potrzeby.Gdy zostanie to wykonane całkowicie w obrębie konstruktora, nigdy nie wystąpi stan pośredni, który
$obj
istnieje, ale nie zostanie zainicjowany, tak jak w systemie opisanym w pytaniu. Lepiej unikać takich stanów pośrednich, ponieważ nie można zagwarantować, że obiekt będzie zawsze używany poprawnie.źródło
Tak naprawdę myślałem o podobnych rzeczach.
Pytanie, które zadałem, brzmiało: „Co robi konstruktor i czy można to zrobić inaczej?” Doszedłem do tych wniosków:
Zapewnia to zainicjowanie niektórych właściwości. Akceptując je jako parametry i ustawiając je. Ale kompilator może to łatwo wymusić. Po prostu dodając adnotacje do pól lub właściwości jako „wymagane”, kompilator sprawdzi podczas tworzenia instancji, czy wszystko jest ustawione poprawnie. Wezwanie do utworzenia instancji prawdopodobnie byłoby takie samo, po prostu nie byłoby żadnej metody konstruktora.
Zapewnia, że właściwości są prawidłowe. Można to łatwo osiągnąć przez stwierdzenie warunku. Znowu po prostu adnotacje właściwości z prawidłowymi warunkami. Niektóre języki już to robią.
Bardziej złożona logika budowy. Nowoczesne wzorce nie zalecają tego w konstruktorze, ale proponują użycie specjalistycznych metod lub klas fabrycznych. Zatem użycie konstruktorów w tym przypadku jest minimalne.
Aby odpowiedzieć na twoje pytanie: Tak, wierzę, że jest to możliwe. Wymagałoby to jednak dużych zmian w projekcie języka.
I właśnie zauważyłem, że moja odpowiedź jest całkiem OT.
źródło
Tak, możesz zrobić prawie wszystko bez użycia konstruktorów, ale wyraźnie marnuje to zalety języków programowania obiektowego.
We współczesnych językach (porozmawiam tutaj o C #, w którym programuję) możesz ograniczyć części kodu, które można uruchomić tylko w konstruktorze. Dzięki temu możesz uniknąć niezdarnych błędów. Jedną z takich rzeczy jest tylko modyfikator:
Zgodnie z zaleceniami Joachima Sauera, zamiast używać
Factory
wzoru, przeczytaj oDependency Injection
. Polecam przeczytanie Dependency Injection w .NET autorstwa Mark Seemann .źródło
Utwórz instancję obiektu z typem w zależności od wymagań, jest to absolutnie możliwe. Może to być sam obiekt, wykorzystujący zmienne globalne systemu, aby zwrócić określony typ.
Jednak mają w kodzie klasę, która może być „Wszystko” to koncepcja typu dynamicznego . Osobiście uważam, że takie podejście powoduje niespójność w kodzie, sprawia, że kompleksy testowe * i „przyszłość staje się niepewna” w odniesieniu do przebiegu proponowanej pracy.
* Mam na myśli fakt, że testy muszą uwzględniać najpierw typ, a następnie wynik, który chcesz osiągnąć. Następnie tworzysz duży test zagnieżdżony.
źródło
Aby zrównoważyć niektóre inne odpowiedzi, twierdząc:
i
Takie stwierdzenia czasami sugerują założenie, że:
Ale to nie do końca prawda. Większość języków nie ma reguły przeciw konstruktorowi przekazującemu
this
(self
lub jakiemukolwiek jemu język to nazwie) kod zewnętrzny. Taki konstruktor całkowicie przestrzega powyższej reguły, ale ryzykuje narażeniem częściowo skonstruowanych obiektów na kod zewnętrzny. To drobiazg, ale łatwo go przeoczyć.źródło
Jest to nieco anegdotyczne, ale zwykle rezerwuję konstruktory na stan niezbędny do tego, aby obiekt był kompletny i użyteczny. Z wyjątkiem iniekcji setera, po uruchomieniu konstruktora mój obiekt powinien być w stanie wykonać wymagane od niego zadania.
Wszystko, co można odroczyć, pomijam w konstruktorze (przygotowuję wartości wyjściowe itp.). Przy takim podejściu nie używam konstruktorów do niczego, ale sensowne jest wstrzykiwanie zależności.
Ma to również dodatkową zaletę polegającą na okablowaniu procesu projektowania mentalnego, aby nie robić nic przedwcześnie. Nie zainicjujesz ani nie wykonasz logiki, która może nigdy nie zostać użyta, ponieważ w najlepszym razie wszystko, co robisz, to podstawowa konfiguracja dla przyszłych prac.
źródło