Czy nazwane argumenty zastępują wzorzec konstruktora?

20

Czy podczas używania języka, który obsługuje argumenty nazwane i opcjonalne, wzorzec konstruktora nie ma już praktycznego zastosowania?

Budowniczy:

new Builder(requiredA, requiredB).setOptionalA("optional").Build();

Argumenty opcjonalne / nazwane:

new Object(requiredA, requiredB, optionalA: "optional");
Paweł Nikonowicz
źródło
3
Jak radzisz sobie z 20 opcjonalnymi argumentami? Nie ma problemu, który Konstruktor musi rozwiązać, dopóki nie stanie się duży. W punkcie, który tu opisałeś, masz dwóch konstruktorów (i nie zbudowałbym Konstruktora dla tak małego problemu).
1
Nawet z opcjonalnymi argumentami - jeśli konstruktor ma więcej niż 2 argumenty, wolę używać obiektu wartości do enkapsulacji konfiguracji. To samo dotyczy płynnych interfejsów użytkownika i konstruktora: wszystko większe niż 3 zostanie zastąpione przez obiekt wartości.
Thomas Junk

Odpowiedzi:

21

Konstruktory są najbardziej przydatne, gdy twój obiekt potrzebuje wielu argumentów / zależności, aby być użytecznym lub jeśli chcesz pozwolić na wiele różnych sposobów konstruowania obiektu.

Z czubka mojej głowy mogę sobie wyobrazić, że ktoś może chcieć „budować” obiekty w takiej grze 3D:

// Just ignore the fact that this hypothetical god class is coupled to everything ever
new ObjectBuilder(x, y, z).importBlenderMesh("./meshes/foo")
                          .syncWithOtherPlayers(serverIP)
                          .compileShaders("./shaders/foo.vert", "./shaders/foo.frag")
                          .makeDestructibleRigidBody(health, weight)
                          ...

Argumentowałbym, że ten przykład jest bardziej czytelny dzięki metodom konstruktora, które właśnie stworzyłem, niż byłoby to z opcjonalnymi parametrami:

new Object(x, y, z, meshType: MESH.BLENDER,
                    meshPath: "./meshes/foo",
                    serverToSyncWith: serverIP,
                    vertexShader: "./shaders/foo.vert",
                    physicsType: PHYSICS_ENGINE.RIGID_DESTRUCTIBLE,
                    health: health,
                    weight: weight)
                    ...

W szczególności informacje sugerowane przez nazwy metod konstruktora muszą zostać zastąpione jeszcze większą liczbą parametrów i o wiele łatwiej jest zapomnieć o jednym parametrze w grupie ściśle powiązanych parametrów. W rzeczywistości brakuje shadera fragmentów, ale nie zauważyłbyś tego, gdybyś nie wiedział, jak go szukać.


Oczywiście, jeśli twój obiekt wymaga tylko jednego do pięciu argumentów do skonstruowania, nie ma potrzeby angażowania wzorca konstruktora, niezależnie od tego, czy podałeś / nie parametry opcjonalne.

Ixrec
źródło
Nie kupuję twoich argumentów. Jeśli nazwy metod konstruktora są tak wspaniałe, możesz ich również użyć do nazw parametrów. Jeśli parametry są ściśle powiązane, umieść je w konstruktorze małych obiektów.
user949300
@ user949300 Myślę, że przegapiłeś ważną część tego punktu, a mianowicie to, że tutaj metody konstruktora opisują relacje między parametrami, które się gubią, jeśli masz tylko kilka parametrów opcjonalnych. W przykładzie konstruktora Ixreca „zdrowie” i „waga” są wyraźnie logicznie częścią ustawień ciała, które można zniszczyć, ale relacja ta gubi się w opcjonalnej wersji argumentu.
Jules
1
nie, jeśli jednym z opcjonalnych parametrów jest (body: new DestructibleRigidBody (zdrowie, waga), ...)
Weyland Yutani
@Jules. Co powiedział Weyland - stwórz dobrze znanego konstruktora, jeśli chodzi o wagę i wzrost.
user949300
8

Oprócz tego, co powiedział Ixrec, konstruktory lub metody o nazwie parametry nie pozwolą ci ustawić obiektu w stanie, który ma zostać zbudowany, w którym można go jeszcze zmodyfikować przed jego zbudowaniem. To jest piękno Konstruktora, w którym możesz przekazać części jego konstrukcji innym metodom lub klasom:

var myThingBuilder = new ThingBuilder("table");
myThingBuilder.setAttribute(Attributes.Legs, 4);

inventoryManager.setPrices(myThingBuilder);

// inventory manager
var availableCheapestMaterial = getMaterial();
myThingBuilder.setMaterial(availableCheapestMaterial);

Zasadniczo możesz także ominąć konstruktora wokół systemu, dopóki nie będzie on gotowy do zbudowania ostatecznego obiektu, co pozwoli ci zmniejszyć ilość wiedzy, którą musi posiadać konstruktor-konsument.

Alfa
źródło
Nie rozumiem twojego ostatniego akapitu. Jeśli „rzucisz konstruktora wokół systemu, aż będzie on gotowy”, ma on zbyt dużą wiedzę na temat systemu. Twój ThingBuilder wie o Atrybutach, jest tajemniczo modyfikowany przez InventoryManager i wie o Materiałach. Nie widzę, jak to zmniejsza wiedzę.
user949300
@ user949300 Pomyśl, jak by to było, gdyby konstruktor nie podróżował przez system. Będziesz zmuszony mieć klasę z ogromnym współczynnikiem fanowskim, a to jest po prostu konieczne, aby zbudować Rzecz. (Oczywiście zakładając, że twoja klasa nie koncentruje całej wiedzy, tego przede wszystkim chcieliśmy uniknąć.) Teraz, jeśli ta klasa ma jakąkolwiek inną odpowiedzialność, tworzysz ogromną klasę, łamiąc S w SOLID . Jeśli to jedyna odpowiedzialność, robisz sobie ThingBuilder.
Alpha
1

To zależy od tego, co robisz z konstruktorem.

Jeśli używasz konstruktora tylko do ustawiania (i zmieniania) właściwości obiektu i (odraczania) tworzenia obiektu, można go zastąpić nazwanymi parametrami.

Zastępując konstruktor, możesz mieć kompromis w zakresie czytelności / użytkowania, o którym wspomniał @Ixrec (lub może go nie mieć, zależy to od tego, co robisz z konstruktorem).

Jeśli jednak konstruktor nie tylko zachowuje właściwości, a każdy etap budowy wymaga logiki, nie można go zastąpić.

MockBuilder to przykład, w którym nie można go zastąpić nazwanymi parametrami. Ze strony:

Logiki kroków tworzenia nie można zastąpić nazwanymi parametrami

rvazquezglez
źródło
-5

Wzorzec konstruktora jest niezbędny podczas pracy z niezmiennymi obiektami. Istnieje wiele korzyści z pracy z niezmiennymi obiektami, szczególnie w zwiększaniu niezawodności programu w środowisku współbieżnym (tj. Wątkach)

codedabbler
źródło
3
Tak, Konstruktory świetnie nadają się do pracy ze złożonymi obiektami niezmiennymi (lub nawet zmiennymi - musisz upewnić się, że obiekt jest w spójnym stanie, zanim będzie można go użyć). Powiedział, że new Integer(42), new BigDecimal("42.000")i new String("foobar")są konstruktorzy do immutables że ... dobrze, konstruktor byłyby niepotrzebnie skomplikowane dla tych przypadkach. Konstruktor nie jest więc niezbędny do pracy z niezmiennym, gdy konstruktory mogą równie dobrze działać.
Tak, konstruktor może być używany z niezmiennymi obiektami, co nie jest odrzucane. Ale pierwotne pytanie brzmiało, czy wzorzec Konstruktora ma jakieś praktyczne zastosowanie oprócz opcjonalnych argumentów, a moją odpowiedzią było wyjaśnienie tego.
codedabbler
2
Pierwotne pytanie nie mówi nic o niezmiennych obiektach. Pyta o nazwane parametry i relacje z nimi z Builderami. Twoja odpowiedź dotyczy budowniczego i jego związku z niezmiennymi obiektami. Z trudem widzę, jak twoja odpowiedź odpowiada na pytanie.