views:

258

answers:

3

If I have a constructor with say 2 required parameters and 4 optional parameters, how can I avoid writing 16 constructors or even the 10 or so constructors I'd have to write if I used default parameters (which I don't like because it's poor self-documentation)? Are there any idioms or methods using templates I can use to make it less tedious? (And easier to maintain?)

+20  A: 

You might be interested in the Named Parameter Idiom.

To summarize, create a class that holds the values you want to pass to your constructor(s). Add a method to set each of those values, and have each method do a return *this; at the end. Have a constructor in your class that takes a const reference to this new class. This can be used like so:

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');
Fred Larson
How would you make that work in a constructor? By using `return this` in the constructor method?
Robert Harvey
Obviously, you can't return anything from a constructor. But you can use a copy, such as `Foo f = Foo().option1().option2();` for example. The example in the link uses a separate class to do the actual creation.
Fred Larson
dash-tom-bang
Couldn't you also make an Options class inside the definition of Person? So the example would be `Person p = Person::Options().name("George").age(57).gender('M');`
Mark Ransom
@Mark Ransom: I don't see any reason why not.
Fred Larson
I think the code example is missing `return *this` after `name_ = n;` and so on?
Kyle
This is a nice solution!
FrustratedWithFormsDesigner
@Kyle: You're right. Fixed.
Fred Larson
+9  A: 

What if you made a parameter object that contained all the fields? Then you could just pass that, and just set whichever fields you need. There's probably a name for that pattern, not sure what it is though...

UPDATE:

Code might look like somewhat this:

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

someObject myObject = new someObject(paramObj);

and inside the someObject constructor you can set defaults for things that were not already set (or raise an error if it was mandatory).

Honestly, I'm not a big fan of this solution, but I've used it once or twice when paramObj made sense by containing a set of data that usually all went together (so we could use it for more than just constructors), and it was better than multiple constructors. I found that it was ugly but it worked, YMMV.

FrustratedWithFormsDesigner
... and this parameter object would set default values in its default constructor.
Danvil
Why is this better than just constructing the original object and setting properties?
Robert Harvey
@Johannes: Isn't that just the Named Parameter Idiom that Fred Larson mentions in his answer?
Robert Harvey
@Robert, right, didn't read the linked page carefully. One more argument to ask for a short code summary of what is linked to :)
Johannes Schaub - litb
@Robert: It's only marginally better, but if the values are often passed around as a group, it sometimes makes sense to bundle them together into a single structure.
FrustratedWithFormsDesigner
@Robert: the advantage is that your main object can be immutable, which is often a goal one strives for in OO design. Also, even if your class is mutable, it obviates the need to verify that the setters for these "parameters" aren't called out of order or at times when they don't make sense given the internal state of the object.
rmeador
@Robert, it's also helpful when the parameters must be validated as a group, i.e. there are certain combinations that would be invalid.
Mark Ransom
+1  A: 

And now for the "Boost has something for it" answer:

The Boost Parameter Library seems to be a good fit to your use case.

Éric Malenfant