I want a general solution on how to avoid insanely high number of constructor arguments. The examples I'm providing here are just examples, and I don't want a specific answer for the exact example here. That being said, my problem is obviously having too many arguments in my constructor.
I have a base class for any type of person (soldiers, wizards, merchants, etc.) called Person
.
My Person
class is fairly simple, it implements the basic things common for every person there is.
Let's say I have the following attributes, and my constructor takes an argument for each of these attributes:
string firstName
string surname
uint health
uint maxHealth
- we don't want anyone to have 999999999 health...uint movementSpeed
- and not everyone runs at same speed, right?So the constructor would look like:
Person::Person(const string &firstName="Missing first name",
const string &surname="Missing surname",
const uint health=100,
const uint maxHealth=100,
const uint movementSpeed=50) :
m_firstName(firstName),
m_surname(surname),
m_health(health),
m_maxHealth(maxHealth),
m_movementSpeed(movementSpeed)
{}
Now imagine a new class, deep down in the inheritance tree, called a Wizard
. Most wizards are there for combat, so they can also attack, etc.
Wizard's constructor could look like this:
Wizard::Wizard(const string &firstName="Missing first name",
const string &surname="Missing surname",
const string &wizardName="Missing wizard name",
const uint health=100,
const uint maxHealth=100,
const uint mana=100,
const uint maxMana=100,
const uint strength=50, // physical damage
const uint witchcraft=50, // spell damage
const uint armor=30, // vs. physical damage
const uint resistance=30, // vs. spell damage
const uint movementSpeed=50) :
Warrior(firstName, surName, health, maxHealth, strength, armor, resistance, movementSpeed),
m_wizardName(wizardName),
m_mana(mana),
m_maxMana(maxMana),
m_witchcraft(witchcraft)
{}
There could be even more arguments, but I suppose you get the point. That might not even look bad, but imagine looking at a stranger's code and seeing something like this:
Wizard *wiz = new Wizard("Tom", "Valedro", "Lord Voldemort", 300, 400, 200, 200, 10, 500, 30, 400, 300)
Someone might say that "it's still not that bad, you can just read the docs!" Imo. that is terrible and makes the code insanely hard to read and even write. Also the order of the arguments gets hard for one to keep track of.
There are few solutions I've thought of, but they have their downsides. One thing would be to use default constructor and give it no arguments at all, then use setters to do the job:
Wizard *wiz = new Wizard;
wiz->setFirstName("Tom");
wiz->setSurname("Valedro");
...
Of course this would create dozens of extra lines of text, and some people are hardly against getters and setters.
It would get rid of the numbers with no meanings tho, atleast you could read what each number does (wiz->setHealth(100);
obviously tells we're setting health here).
Another solution would be to combine some attributes into structs.
I could easily merge firstName
, surname
and wizardName
(even better, use nickname
) into a Name
class or struct.
This would reduce the number of my arguments in this example, but as I said, I want a general answer for such a problem.
There could still be awfully many arguments even after combining some of them, or you might not be able to combine any cause they're not similar at all.
You could use "Fluent Constructors"
See for example this link: http://richarddingwall.name/2009/06/01/fluent-builder-pattern-for-classes-with-long-ish-constructors/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With