Ich betrachte mich immer noch als Programmiererlehrling und bin immer auf der Suche nach einem "besseren" Weg für typisches Programmieren. Heute hat mein Kollege argumentiert, dass mein Codierungsstil unnötige Arbeit leistet, und ich möchte Meinungen von anderen hören. Wenn ich eine Klasse in OOP-Sprache entwerfe (normalerweise C ++ oder Python), teile ich die Initialisierung normalerweise in zwei verschiedene Teile auf:
class MyClass1 {
public:
Myclass1(type1 arg1, type2 arg2, type3 arg3);
initMyClass1();
private:
type1 param1;
type2 param2;
type3 param3;
type4 anotherParam1;
};
// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
: param1(arg1)
, param2(arg2)
, param3(arg3)
{}
// Any other procedure is done in a separate initialization function
MyClass1::initMyClass1() {
// Validate input arguments before calculations
if (checkInputs()) {
// Do some calculations here to figure out the value of anotherParam1
anotherParam1 = someCalculation();
} else {
printf("Something went wrong!\n");
ASSERT(FALSE)
}
}
(oder Python-Äquivalent)
class MyClass1:
def __init__(self, arg1, arg2, arg3):
self.arg1 = arg1
self.arg2 = arg2
self.arg3 = arg3
#optional
self.anotherParam1 = None
def initMyClass1():
if checkInputs():
anotherParam1 = someCalculation()
else:
raise "Something went wrong!"
Wie ist Ihre Meinung zu diesem Ansatz? Sollte ich den Initialisierungsprozess nicht aufteilen? Die Frage ist nicht nur auf C ++ und Python beschränkt, sondern es werden auch Antworten für andere Sprachen geschätzt.
design
object-oriented
coding-style
Caladbolgll
quelle
quelle
Antworten:
Obwohl es manchmal problematisch ist, hat das Initialisieren von allem im Konstruktor viele Vorteile:
initMyClass1()
weil er nicht da ist . "Die billigsten, schnellsten und zuverlässigsten Komponenten sind diejenigen, die es nicht gibt."quelle
Denken Sie an die Abstraktion, die Sie Ihren Benutzern bereitstellen.
Warum etwas aufteilen, was auf einmal gemacht werden kann?
Die zusätzliche Initialisierung ist nur etwas Besonderes für Programmierer, die Ihre API verwenden, um sich daran zu erinnern, und bietet mehr Möglichkeiten, um Fehler zu machen, wenn sie es nicht richtig machen, aber welchen Wert haben sie für diese zusätzliche Belastung?
Sie möchten ganz einfach, benutzerfreundlich und mit schwer zu begehenden Abstraktionen ausstatten. Das Programmieren ist schon schwer genug, ohne dass man sich unnötige Dinge merken muss. Sie möchten, dass Ihre API-Benutzer (auch wenn Sie nur Ihre eigene API verwenden) in die Erfolgsgrube geraten .
quelle
Initialisieren Sie alles außer Big Data. Statische Analysewerkzeuge kennzeichnen Felder, die im Konstruktor nicht initialisiert wurden. Am produktivsten und sichersten ist es jedoch, alle Mitgliedsvariablen mit Standardkonstruktoren zu versehen und nur diejenigen explizit zu initialisieren, die eine nicht standardmäßige Initialisierung erfordern.
quelle
Es gibt Fälle, in denen das Objekt viele Initialisierungen aufweist, die in zwei Kategorien unterteilt werden können:
Attribute, die entweder unveränderlich sind oder nicht zurückgesetzt werden müssen.
Attribute, die möglicherweise auf die ursprünglichen Werte (oder Werte mit Vorlagen) zurückgesetzt werden müssen, basierend auf einer bestimmten Bedingung, nachdem sie ihren Job ausgeführt haben, eine Art Soft-Reset. zB Verbindungen in einem Verbindungspool.
Hier kann der zweite Teil der Initialisierung, der in einer separaten Funktion namens InitialiseObject () gespeichert ist, im ctor aufgerufen werden.
Dieselbe Funktion kann später aufgerufen werden, wenn ein Soft-Reset erforderlich ist, ohne dass das Objekt verworfen und neu erstellt werden muss.
quelle
Wie andere bereits gesagt haben, ist es im Allgemeinen eine gute Idee, im Konstruktor zu initialisieren.
Es gibt jedoch Gründe, die in bestimmten Fällen möglicherweise nicht zutreffen.
Fehlerbehandlung
In vielen Sprachen besteht die einzige Möglichkeit, einen Fehler in einem Konstruktor anzuzeigen, darin, eine Ausnahme auszulösen.
Wenn Ihre Initialisierung eine vernünftige Chance hat, einen Fehler auszulösen, z. B. wenn es sich um eine E / A-Operation handelt oder wenn die Parameter vom Benutzer eingegeben werden, können Sie nur eine Ausnahme auslösen. In einigen Fällen ist dies möglicherweise nicht das, was Sie möchten, und es kann sinnvoller sein, den fehleranfälligen Code auf eine separate Initialisierungsfunktion aufzuteilen.
Das wahrscheinlich häufigste Beispiel hierfür ist C ++, wenn der Projekt- / Organisationsstandard darin besteht, Ausnahmen auszuschalten.
Zustandsmaschine
Dies ist der Fall, wenn Sie ein Objekt mit expliziten Statusübergängen modellieren. Zum Beispiel eine Datei oder ein Socket, der geöffnet und geschlossen werden kann.
In diesem Fall ist es üblich, dass die Objektkonstruktion (und -löschung) nur speicherorientierte Attribute (Dateiname, Port usw.) behandelt. Es wird dann Funktionen zum gezielten Verwalten der Zustandsübergänge geben, z. B. Öffnen, Schließen, die effektiv Initialisierungs- und Abreißfunktionen sind.
Die Vorteile liegen in der Fehlerbehandlung, wie oben, es kann jedoch auch ein Fall vorliegen, bei dem die Konstruktion von der Initialisierung getrennt wird (beispielsweise wenn Sie einen Vektor von Dateien erstellen und diese asynchron öffnen).
Der Nachteil ist, wie andere gesagt haben, dass Sie jetzt die Last der Zustandsverwaltung auf den Benutzer Ihrer Klassen legen. Wenn Sie mit nur einer Konstruktion auskommen könnten, könnten Sie beispielsweise RAII verwenden, um dies automatisch zu tun.
quelle