2010-04-23 9 views
16

Wenn ich einen Konstruktor mit sage 2 erforderlichen Parametern und 4 optionalen Parametern habe, wie kann ich vermeiden, 16 Konstruktoren oder sogar die 10 oder so Konstruktoren zu schreiben, die ich schreiben müsste, wenn ich Standardparameter (was mag ich nicht, weil es eine schlechte Selbstdokumentation ist)? Gibt es irgendwelche Idiome oder Methoden, die Vorlagen verwenden, die ich verwenden kann, um es weniger mühsam zu machen? (Und einfacher zu pflegen?)Vermeiden Sie die Langeweile der optionalen Parameter

+1

Sind Sie sicher, dass Ihre Klasse nicht versucht, zu viel zu tun? –

+0

@Johannes: Er will die Möglichkeit zu überspringen sagen d, ohne den Aufrufer zu zwingen, seinen Standardwert zu kennen. – Danvil

+2

@John: Ich kann sehen, was Sie bekommen, aber Parameter können Eigenschaften sein. Zum Beispiel habe ich eine GUI-Button-Klasse, die viele Eigenschaften wie Bild, Text, Größe, Farbe usw. hat, aber ich fühle nicht, dass es "zu viel macht". IMHO, Tasten dienen einem ganz bestimmten Zweck und sind leicht in einer einzigen Klasse zu passen, wo der Benutzer sofort wissen wird, was die Klasse erreicht. – Kyle

Antwort

34

Sie könnten in der Named Parameter Idiom interessiert sein.

Zusammenfassend erstellen Sie eine Klasse, die die Werte enthält, die Sie an Ihre Konstruktoren übergeben möchten. Fügen Sie eine Methode hinzu, um jeden dieser Werte festzulegen, und lassen Sie jede Methode am Ende eine return *this; ausführen. Haben Sie einen Konstruktor in Ihrer Klasse, der eine konstante Referenz auf diese neue Klasse verwendet. Dies kann wie folgt verwendet werden:

class Person; 

class PersonOptions 
{ 
    friend class Person; 
    string name_; 
    int age_; 
    char gender_; 

public: 
    PersonOptions() : 
    age_(0), 
    gender_('U') 
    {} 

    PersonOptions& name(const string& n) { name_ = n; return *this; } 
    PersonOptions& age(int a) { age_ = a; return *this; } 
    PersonOptions& gender(char g) { gender_ = g; return *this; } 
}; 

class Person 
{ 
    string name_; 
    int age_; 
    char gender_; 

public: 
    Person(const PersonOptions& opts) : 
    name_(opts.name_), 
    age_(opts.age_), 
    gender_(opts.gender_) 
    {} 
}; 
Person p = PersonOptions().name("George").age(57).gender('M'); 
+0

Wie würdest du das in einem Konstruktor machen? Mit 'return this' in der Konstruktormethode? –

+1

Offensichtlich können Sie nichts von einem Konstruktor zurückgeben. Aber Sie können eine Kopie verwenden, zum Beispiel 'Foo f = Foo(). Option1(). Option2();' zum Beispiel. Das Beispiel in der Verknüpfung verwendet eine separate Klasse für die eigentliche Erstellung. –

+2

@Robert ist es wichtig zu beachten, dass die rechte Seite mit "PersonOptions" beginnt, nicht mit "Person". Dies erstellt ein vermutlich leichtgewichtiges Objekt und weist dann eine Reihe von Methodenaufrufen auf, um Eigenschaften auf nicht standardmäßige Werte festzulegen. Dann gibt es einen Ctor namens 'Person (const PersonOptions &)' und ta da. Die Instanziierung eines Objekts (z. B. "PersonOptions()") lässt "* this" im aktuellen Kontext, so dass Sie es sofort (.method()) ausführen können. Jede Methode muss jedoch "zurückgeben", damit sie danach ketten kann. –

9

Was ist, wenn Sie ein Parameterobjekt erstellt haben, das alle Felder enthält? Dann könntest du das einfach weitergeben und einfach die Felder setzen, die du brauchst. Wahrscheinlich gibt es einen Namen für dieses Muster, nicht sicher, was sie jetzt ist ...

UPDATE:

-Code wie etwas aussehen könnte:

paramObj.x=1; 
paramObj.y=2; 
paramObj.z=3; 
paramObj.magic=true; 
... //set many other "parameters here" 

someObject myObject = new someObject(paramObj); 

und innerhalb des someObject Konstruktor können Sie Legen Sie die Standardwerte für Dinge fest, die noch nicht festgelegt wurden (oder melden Sie einen Fehler, wenn dies obligatorisch war).

Ehrlich gesagt, ich bin kein großer Fan dieser Lösung, aber ich habe es ein oder zweimal verwendet, wenn paramObj Sinn gemacht, indem Sie eine Reihe von Daten, die normalerweise alle zusammen (so dass wir es für mehr als nur Konstruktoren), und es war besser als mehrere Konstruktoren. Ich fand, dass es hässlich war, aber es hat funktioniert, YMMV.

+2

... und dieses Parameterobjekt würde Standardwerte in seinem Standardkonstruktor setzen. – Danvil

+1

Warum ist das besser, als nur die ursprünglichen Objekt- und Einstellungseigenschaften zu erstellen? –

+0

@Johannes: Ist das nicht nur das Named Parameter Idiom, das Fred Larson in seiner Antwort erwähnt? –

4

Und jetzt für die „Boost-hat etwas für sich“ Antwort:

Die Boost Parameter Library scheint eine gute Passform zu Ihrem Anwendungsfall zu sein.

1

neue All für C 17 ++

#include <optional> 

using optional_int = std::optional<int>; 

class foo { 
    int arg0, arg1; // required 
    int arg2, arg3; // optional 
    const int default_2 = -2; 
    const int default_3 = -3; 
public: 
    foo(int arg0, int arg1, optional_int opt0 = {}, optional_int opt1 = {}) 
     : arg0(arg0), arg1(arg1) 
     , arg2(opt0.value_or(default_2)) 
     , arg3(opt1.value_or(default_3)) 
    { } 

}; 

int main() { 
    foo bar(42, 43, {}, 45); // Take default for opt0 (arg2) 
    return 0; 
} 

I haben eine kubische Spline-Implementierung, die der Benutzer bei der ersten Ableitung zu spezifizieren, entweder das linke Ende, dem rechten Ende oder beide gegebenenfalls ermöglicht. Wenn eine Ableitung nicht spezifiziert ist, berechnet der Code tatsächlich eine, indem er annimmt, dass die zweite Ableitung Null ist (der sogenannte "natürliche Spline"). Hier ist ein Fragment für das linke Ende.

// Calculate the second derivative at the left end point 
    if (!left_deriv.has_value()) { 
     ddy[0]=u[0]=0.0; // "Natural spline" 
    } else { 
     const real yP0 = left_deriv.value(); 
     ddy[0] = -0.5; 
     u[0]=(3.0/(x[1]-x[0]))*((y[1]-y[0])/(x[1]-x[0])-yP0); 
    }