2009-02-07 6 views
7

Ich begann mit der Formulierung einer Frage: "Was ist der beste Weg, Komponententests an einem Konstruktor durchzuführen (zB __construct() in PHP5)", aber beim Durchlesen der verwandten Fragen sah ich mehrere Kommentare, die darauf hindeuten Das Festlegen von Membervariablen oder Durchführen komplizierter Operationen im Konstruktor sind keine Nos.Was macht man am besten nicht in einem Konstruktor?

Der Konstruktor für die fragliche Klasse nimmt einen Parameter, führt einige Operationen darauf aus (stellt sicher, dass er einen Sniff-Test besteht und wandelt ihn bei Bedarf um) und speichert ihn dann in einer Membervariablen.

Ich dachte, die Vorteile der es auf diese Weise tun, waren:

1), dass Client-Code immer sicher sein würde, einen Wert für diese Membervariable haben, wenn ein Objekt dieser Klasse instanziiert wird, und

2) spart einen Schritt in Client-Code (von denen denkbar verpasst) sein könnte, zum Beispiel

$Thing = new Thing; 
$Thing->initialize($var); 

wenn wir das einfach tun konnten

und damit fertig sein.

Ist dies ein No-No? Wenn ja warum?

Antwort

8

Das kommt ziemlich viel in C++ Diskussionen, und der allgemeine Schlussfolgerung, die ich dort gekommen sind, war dies:

Wenn ein Objekt keine externen Ressourcen nicht erwerben wird, die Mitglieder mussinitialisiert werden in der Konstruktor. Dazu müssen alle Arbeiten im Konstruktor ausgeführt werden.

  • (x, y) Koordinate (oder wirklich jede andere Struktur, die nur ein besserer Tupel ist)
  • US-Staatsabkürzung Lookup-Tabelle

Wenn ein Objekt erwirbt Ressourcen, die sie kontrollieren können, sie kannin den Konstruktor zugeordnet werden:

  • offene Dateideskriptor
  • zugewiesenen Speicher
  • Griff/Zeiger in eine externe Bibliothek

Wenn das Objekt erwirbt Ressourcen, die sie die Kontrolle nicht ganz kann sie müssenaußerhalb des Konstruktors zugeordnet werden:

  • TCP-Verbindung
  • DB-Verbindung
  • wir ak Referenz

Es gibt immer Ausnahmen, aber dies deckt die meisten Fälle ab.

-1

Sie sollten Dinge nicht in einen Konstruktor einfügen, der nur einmal ausgeführt werden soll, wenn die Klasse erstellt wird.

Zu erklären.

Wenn ich eine Datenbankklasse hätte. Wo der Konstruktor ist die Verbindung zur Datenbank So

$db = new dbclass; 

und jetzt bin ich mit der Datenbank verbunden.

Dann haben wir eine Klasse, die einige Methoden innerhalb der Datenbankklasse verwendet.

class users extends dbclass 
{ 
    // some methods 
} 

$users = new users 
// by doing this, we have called the dbclass's constructor again 
+0

In diesem Fall würde ich eher in einer Instanz von dbclass passieren, oder es als Singleton verwenden, anstatt zu erweitern die Klasse selbst. – Ross

+0

Ja, es gibt Möglichkeiten, aber das ist nur ein Beispiel. –

+0

Wenn Sie diese Funktionalität nicht möchten, können Sie den Konstruktor im Benutzerobjekt überschreiben. Auch einige Sprachen, wie C#, erben den Konstruktor überhaupt nicht. –

6

Konstruktoren für das Objekt zu initialisieren, so

$Thing = new Thing($var); 

durchaus akzeptabel ist.

16

Meine Faustregel ist, dass ein Objekt nach dem Ende des Konstruktors einsatzbereit sein sollte. Aber es gibt oft eine Reihe von Optionen, die anschließend optimiert werden können.

Meine Liste von Ge-und donts:

  • Konstrukteurs sollten grundlegende Optionen für das Objekt einzurichten.
  • Sie sollten vielleicht Instanzen von Hilfsobjekten erstellen.
  • Sie sollten nicht aqquire Ressourcen (Dateien, Sockets, ...), es sei denn, das Objekt ist eindeutig ein Wrapper um einige Ressourcen.

Natürlich keine Regeln ohne Ausnahmen. Wichtig ist, dass Sie über Ihr Design und Ihre Wahl nachdenken. Machen Sie die Verwendung von Objekten natürlich - und dazu gehört die Fehlerberichterstattung.

2

Um die Testbarkeit einer Klasse zu verbessern, ist es generell eine gute Sache, den Konstruktor so einfach wie möglich zu halten und nur nach Dingen fragen zu lassen, die er wirklich benötigt. Im Rahmen der "Clean Code Talks" -Serie von Google gibt es eine ausgezeichnete presentation, die im Detail auf YouTube veröffentlicht wird.

+1

+1 stimme ich zu. Wir haben Tonnen von Code, wo Konstruktoren tatsächlich Dateien direkt öffnen, um einige Konfigurationsinformationen zu lesen. In gewisser Weise macht es diese Objekte bequem zu verwenden, aber Schreibeinheit Tests wäre unmöglich. Gut, dass wir keine Unit-Tests haben ;-) –

0

Hängt davon ab, welche Art von System Sie versuchen zu entwerfen, aber im Allgemeinen glaube ich, Konstruktoren werden am besten nur für die Initialisierung des "Zustands" des Objekts verwendet, aber nicht selbst Zustandsübergänge durchführen. Am besten lassen Sie es einfach die Standardeinstellungen festlegen.

Ich schreibe dann eine "handle" -Methode in meine Objekte für die Handhabung von Dingen wie Benutzereingaben, Datenbankaufrufe, Ausnahmen, Kollatierung, was auch immer. Der Grundgedanke ist, dass dies jeden Zustand, in dem sich das Objekt befindet, basierend auf externen Kräften (Benutzereingaben, Zeit usw.) handhabt. Grundsätzlich werden alle Dinge, die den Zustand des Objekts ändern und zusätzliche Aktionen erfordern, in den Objekt.

Schließlich legte ich eine Rendermethode in die Klasse, um dem Benutzer etwas Sinnvolles zu zeigen. Dies stellt nur den Zustand des Objekts für den Benutzer dar (was auch immer das sein mag).)

__construct (Argumente $)
handle()
render (Exception $ ex = null)

0

Die __construct magische Methode ist in Ordnung zu verwenden. Der Grund für die Initialisierung in vielen Frameworks und Anwendungen liegt darin, dass das Objekt auf eine Schnittstelle programmiert wird oder versucht, ein Singleton/getInstance-Muster zu erstellen.

Diese Objekte werden im Allgemeinen in einen Kontext oder einen Controller gezogen und dann von anderen Objekten auf höherer Ebene mit der allgemeinen Schnittstellenfunktionalität aufgerufen.

1

Sie auf jeden Fall vermeiden sollten Sie den Client zu machen haben

$thing->initialize($var) 

Diese Art von Sachen nennen absolut im Konstruktor gehört. Es ist einfach unfreundlich gegenüber dem Client-Programmierer, um sie dazu zu bringen, dies anzurufen. Es gibt eine (etwas umstrittene) Denkschule, die besagt, dass Sie Klassen schreiben sollten, so dass Objekte niemals in einem ungültigen Zustand sind - und "nicht initialisiert" ist ein ungültiger Zustand.

Aus Gründen der Testbarkeit und der Leistung ist es jedoch manchmal sinnvoll, bestimmte Initialisierungen auf später im Objekt zu verschieben. In Fällen wie diesen ist faule Bewertung die Lösung.

Apologies für Java-Syntax in einer Python Antwort setzen, aber:

// Constructor 
public MyObject(MyType initVar) { 
     this.initVar = initVar; 
} 

private void lazyInitialize() { 
    if(initialized) { 
     return 
    } 
    // initialization code goes here, uses initVar 
} 

public SomeType doSomething(SomeOtherType x) { 
    lazyInitialize(); 
    // doing something code goes here 
} 

Sie können Ihre Segment verzögerte Initialisierung, so dass nur die Teile, die sie benötigen initialisiert erhalten. Es ist zum Beispiel üblich, dies in Gettern zu tun, nur für das, was den Wert beeinflusst, der erhalten wird.

4

Die Aufgabe eines Konstruktors besteht darin, die Instanz invariants einer Instanz einzurichten.

Alles was dazu nicht beiträgt, wird am besten außerhalb des Konstruktors gehalten.

0

Wenn $ var unbedingt notwendig ist, für $ Sache zu arbeiten, dann ist es ein DO

Verwandte Themen