Angenommen, aus irgendeinem Grund werden alle Objekte auf diese Weise erstellt: $ obj = CLASS :: getInstance (). Dann injizieren wir Abhängigkeiten mit Setzern und starten die Initialisierung mit $ obj-> initInstance (); Gibt es echte Probleme oder Situationen, die nicht gelöst werden können, wenn wir überhaupt keine Konstruktoren verwenden?
Ps der Grund für das Erstellen von Objekten auf diese Weise ist, dass wir die Klasse in getInstance () nach bestimmten Regeln ersetzen können.
Ich arbeite in PHP, wenn das wichtig ist
object-oriented-design
class-design
Axel Foley
quelle
quelle
Antworten:
Ich würde sagen, dass dies Ihren Designraum stark behindert.
Konstruktoren sind ein großartiger Ort, um übergebene Parameter zu initialisieren und zu validieren. Wenn Sie sie dafür nicht mehr verwenden können, wird die Initialisierung der Statusverarbeitung (oder das Verweigern des Konstruktors "defekter" Objekte) viel schwieriger und teilweise unmöglich.
Wenn beispielsweise jedes
Foo
Objekt ein benötigt,Frobnicator
prüft es möglicherweise in seinem Konstruktor, ob das ObjektFrobnicator
nicht null ist. Wenn Sie den Konstruktor wegnehmen, wird es schwieriger zu überprüfen. Prüfen Sie an jedem Punkt, wo es verwendet werden würde? In einerinit()
Methode (effektiv Externalisierung der Konstruktormethode)? Überprüfen Sie es nie und hoffen Sie auf das Beste?Während Sie wahrscheinlich immer noch alles implementieren könnten (schließlich sind Sie immer noch vollständig), werden einige Dinge viel schwieriger zu tun sein.
Persönlich würde ich vorschlagen, sich mit Abhängigkeitsinjektion / Inversion der Kontrolle zu befassen . Diese Techniken ermöglichen auch das Wechseln konkreter Implementierungsklassen, verhindern jedoch nicht das Schreiben / Verwenden von Konstruktoren.
quelle
HttpClient
, prüft er, ob dieser Parameter nicht null ist). Und wenn diese Bedingungen nicht erfüllt sind, kann dies eine Ausnahme auslösen. Das ist mit dem Konstrukt-und-Set-Werte-Ansatz nicht wirklich möglich.init()
, was durchaus möglich ist - obwohl dies nur einen größeren Wartungsaufwand bedeutet.2 Vorteile für Konstrukteure:
Konstruktoren ermöglichen die atomare Ausführung der Konstruktionsschritte eines Objekts.
Ich könnte einen Konstruktor umgehen und für alles Setter verwenden, aber was ist mit den obligatorischen Eigenschaften, wie Joachim Sauer vorgeschlagen hat? Mit einem Konstruktor besitzt ein Objekt eine eigene Konstruktionslogik, um sicherzustellen, dass es keine ungültigen Instanzen einer solchen Klasse gibt .
Wenn beim Erstellen einer Instanz von
Foo
3 Eigenschaften festgelegt werden müssen, kann der Konstruktor einen Verweis auf alle 3 nehmen, diese validieren und eine Ausnahme auslösen, wenn sie ungültig sind.Verkapselung
Wenn Sie sich ausschließlich auf Setter verlassen, liegt die Last beim Verbraucher eines Objekts, um es richtig zu bauen. Es kann verschiedene Kombinationen von Eigenschaften geben, die gültig sind.
Beispielsweise
Foo
benötigt jede Instanz entweder eine Instanz vonBar
als Eigenschaftbar
oder eine Instanz vonBarFinder
als EigenschaftbarFinder
. Es kann eines von beiden verwenden. Sie können für jeden gültigen Parametersatz einen Konstruktor erstellen und die Konventionen auf diese Weise durchsetzen.Die Logik und Semantik für die Objekte befindet sich im Objekt selbst. Es ist eine gute Verkapselung.
quelle
Ja, Sie können ohne Konstrukteure leben.
Sicher, es kann vorkommen, dass Sie eine Menge doppelten Kesselschildcodes haben. Und wenn Ihre Anwendung von beliebiger Größe ist, werden Sie wahrscheinlich viel Zeit damit verbringen, die Ursache eines Problems zu lokalisieren, wenn dieser Code nicht in der gesamten Anwendung konsistent verwendet wird.
Aber nein, Sie brauchen Ihre eigenen Konstrukteure nicht unbedingt. Natürlich brauchen Sie auch keine Klassen und Objekte.
Wenn Sie nun eine Art Factory-Muster für die Objekterstellung verwenden möchten, schließt sich dies nicht gegenseitig aus, wenn Sie beim Initialisieren Ihrer Objekte Konstruktoren verwenden.
quelle
Der Vorteil der Verwendung von Konstruktoren besteht darin, dass sie es einfacher machen, sicherzustellen, dass Sie niemals ein ungültiges Objekt haben.
Ein Konstruktor gibt Ihnen die Möglichkeit, alle Mitgliedsvariablen des Objekts in einen gültigen Zustand zu versetzen. Wenn Sie dann sicherstellen, dass keine Mutator-Methode das Objekt in einen ungültigen Status ändern kann, wird es nie ein ungültiges Objekt geben, wodurch Sie vor vielen Fehlern bewahrt werden.
Wenn jedoch ein neues Objekt in einem ungültigen Zustand erstellt wird und Sie einige Setter aufrufen müssen, um es in einen gültigen Zustand zu versetzen, in dem es verwendet werden kann, riskieren Sie, dass ein Konsument der Klasse vergisst, diese Setter aufzurufen, oder sie falsch und falsch aufruft Sie erhalten ein ungültiges Objekt.
Eine Problemumgehung könnte darin bestehen, Objekte nur mit einer Factory-Methode zu erstellen, die jedes erstellte Objekt auf Gültigkeit überprüft, bevor es an den Aufrufer zurückgegeben wird.
quelle
Ich denke, Sie machen das schwieriger, als es sein muss. Abhängigkeiten können problemlos über den Konstruktor eingefügt werden. Wenn Sie viele davon haben, verwenden Sie einfach eine wörterbuchähnliche Struktur, damit Sie angeben können, welche Sie verwenden möchten:
Und innerhalb des Konstruktors können Sie die Konsistenz folgendermaßen sicherstellen:
$args
kann dann verwendet werden, um private Mitglieder nach Bedarf festzulegen.Wenn dies vollständig innerhalb des Konstruktors geschieht, wird es niemals einen Zwischenzustand geben, in dem es
$obj
existiert, der jedoch nicht initialisiert wird, wie dies bei dem in der Frage beschriebenen System der Fall wäre. Es ist besser, solche Zwischenzustände zu vermeiden, da Sie nicht garantieren können, dass das Objekt immer korrekt verwendet wird.quelle
Ich habe tatsächlich über ähnliche Dinge nachgedacht.
Die Frage, die ich gestellt habe, lautete: "Was macht der Konstruktor und ist es möglich, es anders zu machen?" Ich bin zu folgenden Schlussfolgerungen gekommen:
Dadurch wird sichergestellt, dass einige Eigenschaften initialisiert werden. Indem Sie sie als Parameter akzeptieren und einstellen. Dies kann jedoch leicht vom Compiler erzwungen werden. Durch einfaches Annotieren der Felder oder Eigenschaften als "erforderlich" würde der Compiler während der Erstellung der Instanz prüfen, ob alles richtig eingestellt ist. Der Aufruf zum Erstellen der Instanz wäre wahrscheinlich derselbe, es gäbe einfach keine Konstruktormethode.
Stellt sicher, dass die Eigenschaften gültig sind. Dies könnte leicht erreicht werden, indem Bedingung geltend gemacht wird. Auch hier würden Sie die Eigenschaften nur mit den richtigen Bedingungen versehen. Einige Sprachen tun dies bereits.
Etwas komplexere Konstruktionslogik. Moderne Patterns empfehlen, dies nicht im Konstruktor zu tun, sondern spezialisierte Factory-Methoden oder -Klassen zu verwenden. Der Einsatz von Konstruktoren ist in diesem Fall also minimal.
Um Ihre Frage zu beantworten: Ja, ich glaube, es ist möglich. Aber es würde einige große Änderungen im Sprachdesign erfordern.
Und mir ist gerade aufgefallen, dass meine Antwort ziemlich OT ist.
quelle
Ja, Sie können fast alles tun, ohne Konstruktoren zu verwenden. Dies ist jedoch ein klarer Vorteil objektorientierter Programmiersprachen.
In modernen Sprachen (ich werde hier auf C # eingehen, in dem ich programmiere) können Sie Codeteile einschränken, die nur in einem Konstruktor ausgeführt werden können. Dank dessen können Sie ungeschickte Fehler vermeiden. Eines davon ist readonly modifier:
Wie von Joachim Sauer zuvor empfohlen, anstatt
Factory
Designmuster zu verwenden, lesen Sie weiterDependency Injection
. Ich würde empfehlen, Dependency Injection in .NET von Mark Seemann zu lesen .quelle
Instanziieren Sie ein Objekt mit einem Typ, der von den Anforderungen abhängt, die absolut möglich sind. Es könnte das Objekt selbst sein, das globale Variablen des Systems verwendet, um einen bestimmten Typ zurückzugeben.
Eine Klasse im Code, die "All" sein kann, ist das Konzept des dynamischen Typs . Ich persönlich glaube, dass dieser Ansatz zu Inkonsistenzen in Ihrem Code führt, die Testkomplexe * erstellt und "die Zukunft wird ungewiss" in Bezug auf den Fluss der vorgeschlagenen Arbeit.
* Ich beziehe mich auf die Tatsache, dass die Tests erstens den Typ und zweitens das gewünschte Ergebnis berücksichtigen müssen. Dann erstellen Sie einen großen verschachtelten Test.
quelle
Um einige der anderen Antworten auszugleichen, behaupten Sie:
und
Solche Aussagen implizieren manchmal die Annahme, dass:
Das ist aber nicht ganz richtig. Die meisten Sprachen haben keine Regel gegen einen Konstruktor, der
this
(self
oder wie auch immer die Sprache es nennt) an externen Code übergibt . Ein solcher Konstruktor hält sich vollständig an die oben angegebene Regel und riskiert dennoch, halbkonstruierte Objekte externem Code auszusetzen. Es ist ein kleiner Punkt, aber leicht zu übersehen.quelle
Dies ist etwas anekdotisch, aber ich behalte mir normalerweise Konstruktoren für einen Zustand vor, der für die Vollständigkeit und Verwendbarkeit des Objekts wesentlich ist. Abgesehen von Setter-Injection sollte mein Objekt nach Ausführung des Konstruktors in der Lage sein, die dafür erforderlichen Aufgaben auszuführen.
Alles, was aufgeschoben werden kann, lasse ich aus dem Konstruktor heraus (Vorbereiten von Ausgabewerten usw.). Bei diesem Ansatz halte ich Konstruktoren für nichts anderes als die Abhängigkeitsinjektion für sinnvoll.
Dies hat auch den zusätzlichen Vorteil, dass Sie Ihren mentalen Entwurfsprozess fest verdrahten, um nichts vorzeitig zu tun. Sie werden keine Logik initialisieren oder ausführen, die möglicherweise nie verwendet wird, da Sie bestenfalls nur die Grundeinstellungen für die bevorstehende Arbeit vornehmen.
quelle