Gibt es einen Grund, eine init()
Methode für einen Typ zu deklarieren ?
Ich frage nicht, ob wir einen Konstruktor vorzieheninit()
sollen oder wieinit()
wir es vermeiden sollen, dies zu deklarieren .
Ich frage , ob es irgendwelche Gründe eine hinter erklärt init()
Methode ( zu sehen , wie häufig es ist) oder wenn es ein Codegeruch und sollte vermieden werden.
Die init()
Redewendung ist weit verbreitet, aber ich habe noch keinen wirklichen Nutzen zu sehen.
Ich spreche von Typen, die die Initialisierung über eine Methode fördern:
class Demo {
public void init() {
//...
}
}
Wann wird dies jemals im Produktionscode von Nutzen sein?
Ich glaube, es könnte ein Codegeruch sein, da der Konstruktor das Objekt nicht vollständig initialisiert, was zu einem teilweise erstellten Objekt führt. Das Objekt sollte nicht existieren, wenn sein Status nicht gesetzt ist.
Dies lässt mich glauben, dass dies Teil einer Technik ist, die zur Beschleunigung der Produktion im Sinne von Unternehmensanwendungen eingesetzt wird. Es ist der einzig logische Grund, warum ich mir eine solche Redewendung vorstellen kann. Ich bin mir nur nicht sicher, wie es vorteilhaft wäre, wenn dies der Fall wäre.
init()
Befindet sich die Methode in einer Klasse, die zu einer Vererbungshierarchie gehört? Ruft die Basisklasse die abgeleitete Klasse auf oder umgekehrt?) In diesem Fall kann die Basisklasse beispielsweise einen "Post-Konstruktor" ausführen "der nur ausgeführt werden kann, nachdem die am meisten abgeleitete Klasse die Konstruktion beendet hat. Dies ist ein Beispiel für eine mehrphasige Initialisierung.Antworten:
Ja, es ist ein Codegeruch. Ein Codegeruch muss nicht unbedingt immer entfernt werden. Es ist etwas, das Sie dazu bringt, einen zweiten Blick darauf zu werfen.
Hier haben Sie ein Objekt in zwei grundverschiedenen Zuständen: vor und nach dem Start. Diese Staaten haben unterschiedliche Zuständigkeiten, unterschiedliche Methoden, die aufgerufen werden dürfen, und unterschiedliches Verhalten. Es sind effektiv zwei verschiedene Klassen.
Wenn Sie sie physisch in zwei separate Klassen unterteilen, entfernen Sie statisch eine ganze Klasse potenzieller Bugs, was dazu führt, dass Ihr Modell möglicherweise nicht ganz so genau mit dem Modell der "realen Welt" übereinstimmt. Sie nennen in der Regel die ersten
Config
oderSetup
oder so ähnlich.Versuchen Sie also das nächste Mal, Ihre construct-init-Idiome in Zwei-Klassen-Modelle umzugestalten, und sehen Sie, wie es für Sie ausgeht.
quelle
this
Daher versuche ich, dies so allgemein wie möglich zu halten (z. B. wie das Aufrufen des Konstruktors ein Codegeruch ist und zu fehleranfälligem Code führen kann). und es wird empfohlen, dies zu vermeiden, unabhängig davon, unter welche Domäne Ihr Projekt fällt).Es hängt davon ab, ob.
Eine
init
Methode ist ein Codegeruch, wenn die Objektinitialisierung nicht vom Konstruktor getrennt werden muss. Manchmal ist es sinnvoll, diese Schritte zu trennen.Eine schnelle Google-Suche brachte mir dieses Beispiel. Ich kann mir leicht weitere Fälle vorstellen, in denen der während der Objektzuweisung ausgeführte Code (der Konstruktor) möglicherweise besser von der Initialisierung selbst getrennt ist. Möglicherweise haben Sie ein geebnetes System, und die Zuordnung / Konstruktion erfolgt in Ebene X, die Initialisierung jedoch nur in Ebene Y, da nur Y die erforderlichen Parameter bereitstellen kann. Möglicherweise ist "init" kostspielig und muss nur für eine Teilmenge der zugewiesenen Objekte ausgeführt werden, und die Ermittlung dieser Teilmenge kann nur in Stufe Y erfolgen. Oder Sie möchten die (virtuelle) "init" -Methode in einer abgeleiteten Methode überschreiben Klasse, die mit einem Konstruktor nicht durchgeführt werden kann. Möglicherweise stellt Ihnen Stufe X zugewiesene Objekte aus einem Vererbungsbaum zur Verfügung, aber Stufe Y kennt die konkrete Ableitung nicht, sondern nur die gemeinsame Schnittstelle (wo
init
vielleicht definiert).Nach meiner Erfahrung stellen diese Fälle natürlich nur einen kleinen Prozentsatz des Standardfalls dar, in dem die gesamte Initialisierung direkt im Konstruktor durchgeführt werden kann. Wenn Sie eine separate
init
Methode sehen, ist es möglicherweise eine gute Idee, deren Notwendigkeit in Frage zu stellen.quelle
init
Methode. Wenn Sie jedoch eine solche Methode sehen, können Sie deren Notwendigkeit in Frage stellen.init()
Methode richtig angewendet würde, würde ich sicher davon profitieren, wenn ich etwas über ihren Zweck erfahre. Entschuldigen Sie meine Unwissenheit, ich bin nur erstaunt darüber, wie schwer es mir fällt, eine Verwendung dafür zu finden, und ich kann nicht darüberMeine Erfahrung gliedert sich in zwei Gruppen:
In meiner persönlichen Erfahrung habe ich nur ein paar Fälle von (1) gesehen, aber viel mehr Fälle von (2). Folglich gehe ich normalerweise davon aus, dass ein init () ein Code-Geruch ist, aber das ist nicht immer der Fall. Manchmal kommt man einfach nicht darum herum.
Ich habe festgestellt, dass die Verwendung des Builder-Musters häufig dazu beitragen kann, die Notwendigkeit / den Wunsch nach einem init () zu beseitigen.
quelle
init()
Methode dies beheben? Dieinit()
Methode würde entweder Parameter benötigen, um Abhängigkeiten zu akzeptieren, oder Sie müssten die Abhängigkeiten innerhalb derinit()
Methode instanziieren , was Sie auch mit einem Konstruktor tun könnten. Könnten Sie ein Beispiel geben?Ein typisches Szenario, in dem sich eine Init-Methode als nützlich erweist, ist, wenn Sie eine Konfigurationsdatei haben, die Sie ändern möchten, und die Änderung berücksichtigt werden soll, ohne die Anwendung neu zu starten. Dies bedeutet natürlich nicht, dass eine Init-Methode getrennt von einem Konstruktor aufgerufen werden muss. Sie können eine Init-Methode von einem Konstruktor aus aufrufen und sie später aufrufen, wenn / wenn sich die Konfigurationsparameter ändern.
Fazit: Wie bei den meisten Dilemmata hängt es von der Situation und den Umständen ab, ob es sich um einen Codegeruch handelt oder nicht.
quelle
Config
?update
/reload
würde wahrscheinlich für diese Art von Verhalten mehr beschreibend sein) , um tatsächlich jene Änderungen registrieren . In diesem Fall würde diese Benachrichtigung dazu führen, dass sich die Konfigurationswerte in Ihrer Anwendung intern ändern, was meiner Meinung nach dadurch zu beobachten ist, dass Ihre Konfiguration beobachtbar ist. Dadurch werden Beobachter benachrichtigt, wenn die Konfiguration angewiesen wird, einen ihrer Werte zu ändern. Oder missverstehe ich Ihr Beispiel?Kommt darauf an, wie du sie benutzt.
Ich verwende dieses Muster in Sprachen mit Speicherbereinigung wie Java / C #, wenn ich ein Objekt nicht ständig auf dem Heap neu zuordnen möchte (z. B. wenn ich ein Videospiel mache und die Leistung hoch halten möchte, Garbage Collectors die Leistung töten). Ich verwende den Konstruktor, um andere benötigte Heap-Zuordnungen vorzunehmen und
init
den grundlegenden nützlichen Status unmittelbar vor jeder Wiederverwendung zu erstellen. Dies hängt mit dem Konzept der Objektpools zusammen.Es ist auch nützlich, wenn Sie über mehrere Konstruktoren verfügen, die eine gemeinsame Teilmenge der Initialisierungsanweisungen verwenden, in diesem Fall
init
jedoch privat sind. Auf diese Weise kann ich jeden Konstruktor so weit wie möglich minimieren, sodass jeder nur seine eindeutigen Anweisungen und einen einzigen Aufruf enthält,init
um den Rest zu erledigen.Im Allgemeinen ist es jedoch ein Codegeruch.
quelle
reset()
Methode für Ihre erste Aussage nicht aussagekräftiger? Was den zweiten betrifft (viele Konstruktoren), so ist das Vorhandensein vieler Konstruktoren ein Codegeruch. Es wird davon ausgegangen, dass das Objekt mehrere Zwecke / Verantwortlichkeiten hat, was auf eine SRP-Verletzung hinweist. Ein Objekt sollte eine Verantwortung haben, und der Konstruktor sollte die erforderlichen Abhängigkeiten für diese eine Verantwortung definieren. Wenn Sie aufgrund optionaler Werte mehrere Konstruktoren haben, sollten diese teleskopieren (was auch ein Codegeruch ist, sollte stattdessen einen Builder verwenden).string
Konstruktorliste einer beliebigen Sprache an , mit unzähligen Optionen. Für mich sind es normalerweise maximal 3 Konstruktoren, aber die gemeinsame Untermenge von Anweisungen als Initialisierung ist sinnvoll, wenn sie sich einen Code teilen, sich aber in irgendeiner Weise unterscheiden.String
könnte dies gelöst werden, indem die Erstellung von Zeichenfolgen entkoppelt wird. Am Ende ist aString
einString
und sein Konstruktor sollte nur das akzeptieren, was für die Ausführung nach Bedarf erforderlich ist. Die meisten dieser Konstruktoren werden für Konvertierungszwecke bereitgestellt, was ein Missbrauch von Konstruktoren ist. Konstruktoren sollten keine Logik ausführen. Andernfalls besteht die Gefahr, dass die Initialisierung fehlschlägt und Sie ein unbrauchbares Objekt erhalten.init()
Methoden können durchaus sinnvoll sein, wenn Sie Objekte haben, die externe Ressourcen benötigen (z. B. eine Netzwerkverbindung), die gleichzeitig von anderen Objekten verwendet werden. Möglicherweise möchten / müssen Sie die Ressource während der gesamten Lebensdauer des Objekts nicht auslasten. In solchen Situationen möchten Sie die Ressource möglicherweise nicht im Konstruktor zuordnen, wenn die Ressourcenzuordnung wahrscheinlich fehlschlägt.Insbesondere in der eingebetteten Programmierung möchten Sie einen deterministischen Speicherbedarf haben. Daher ist es gängige (gute?) Praxis, Konstruktoren frühzeitig, möglicherweise sogar statisch, aufzurufen und erst später zu initialisieren, wenn bestimmte Bedingungen erfüllt sind.
Abgesehen von solchen Fällen denke ich, dass alles in einen Konstruktor gehen sollte.
quelle
init
Methoden gänzlich zu verurteilen ist zu einschränkend, IMHO.Generell bevorzuge ich einen Konstruktor, der alle für eine Funktionsinstanz erforderlichen Argumente erhält. Das macht alle Abhängigkeiten dieses Objekts deutlich.
Andererseits verwende ich ein einfaches Konfigurationsframework, das einen parameterlosen öffentlichen Konstruktor und Schnittstellen zum Einfügen von Abhängigkeiten und Konfigurationswerten erfordert. Danach ruft das Konfigurationsframework die
init
Methode des Objekts auf: Jetzt haben Sie alles erhalten, was ich für Sie habe. Führen Sie die letzten Schritte aus, um sich für die Arbeit vorzubereiten. Aber beachten Sie: Es ist das Konfigurationsframework, das die init-Methode automatisch aufruft, so dass Sie nicht vergessen werden, sie aufzurufen.quelle
Es gibt keinen Codegeruch, wenn die init () - Methode semantisch in den Statuslebenszyklus des Objekts eingebettet ist.
Wenn Sie init () aufrufen müssen, um das Objekt in einen konsistenten Zustand zu versetzen, handelt es sich um einen Codegeruch.
Es gibt mehrere technische Gründe für die Existenz einer solchen Struktur:
quelle
Der Name init kann manchmal undurchsichtig sein. Nehmen wir ein Auto und einen Motor. Um das Auto zu starten (nur einschalten, Radio hören), müssen Sie sicherstellen, dass alle Systeme betriebsbereit sind.
Sie konstruieren also einen Motor, eine Tür, ein Rad usw. Ihr Bildschirm zeigt Motor = Aus.
Es ist nicht erforderlich, den Motor usw. zu überwachen, da diese alle teuer sind. Wenn Sie dann den Zündschlüssel drehen, rufen Sie engine-> start auf. Es werden alle teuren Prozesse ausgeführt.
Jetzt sehen Sie engine = on. Und der Prozess für die Zündung beginnt.
Das Auto lässt sich nicht starten, wenn der Motor nicht verfügbar ist.
Sie können den Motor durch Ihre komplexe Berechnung ersetzen. Wie eine Excel-Zelle. Es müssen nicht immer alle Zellen mit allen Ereignishandlern aktiv sein. Wenn Sie sich auf eine Zelle konzentrieren, können Sie sie starten. Auf diese Weise die Leistung steigern.
quelle