Advertisement

How to initialize a class member variable when using compositon?

Started by March 22, 2015 09:04 AM
15 comments, last by WozNZ 9 years, 10 months ago

I disagree, the important part is to clearly document your constructors with doxygen or similar style comments, so that a programmer using the class can clearly see what it will do when constructed.

I personally don't care for implicit heavy lifting for that reason exactly, when I make a game class and fill it with objects I want to populate them and set them up when I run the actual game code, not simply the second I declare an instance of it in the main function.

I also find it particularly annoying that you have to look at the documentation for and read about every constructor just to know that it isn't doing a bunch of heavy lifting, and if it is then it forces that responsibility on any class that contains an instance of it. A window is a good example, if my game class has a window I shouldn't have to make it a pointer and call new just to be able to control when the window is actually created and shown, it shouldn't automagically do it just by being a member. I find that a very unflexible interface.

Also, I have no aversion to using exceptions if clearly documented. Exceptions are perfect for throwing errors from a constructor.

I've written code that throws from constructors, but the more chances you have to throw the bigger chance you'll end up in a spiral of death with one constructor throwing into another constructor. Frankly I just don't see a point to doing so unless there is an explicit need to do it in the constructor.


one constructor throwing into another constructor

I usually do this for startup errors. Anything I create on startup can throw on failure and main() contains the try...catch. This way it doesn't matter too much if you forget to free something or the exception causes it to not delete something, as exiting your app back to the os will free everything anyway.

Everyone's style differs and this is mine. I am sure my love of exceptions and heavy ctors would get me lynched by some :)

Advertisement

For composition my norm is to pass in the initialised child objects to the constructor, if an object is such that it require complex construction then use a Factory/Builder that performs the construction for you to keep that self contained.

One problem I see with the OPs initial sample being that if you are using MutableState in your objects, the OO norm, then caching values like "area" is very dangerous. How do you detect that Width or Height had changed and hence makes your area invalid.

Given the simplicity of "area" why not calculate on the fly each time to protect against this.

If you are dealing with immutable objects this caching of results makes more sense as you know with 100% certainty that nothing will change underfoot and hence your valid will remain valid.

Thanks guys, learn't a lot from this, as I often do when the more deeper concepts are being discussed in relation to my question...

Just for clarification, on braindigitalis's post

Do you mean something like this?

class ShapeArea
{
private:
Rectangle rectShape;
int area;

public:
ShapeArea(const Rectshape &rs) : rectShape(rs), area(rs.getArea())
{
}
};


ShapeArea n(myrectshape);

Is this essentially saying that the constructor ShapeArea(const Rectangle &rs), needs a reference to a Rectangle object that already exists (since as far as I know I cannot pass in a reference if it does not yet exist). Therefore I cant actually create the ShapeArea class until a Rectangle exists somewhere, which stops me from having a stuffed up ShapeArea object (as I could previously).

Is this what I should be doing, for every class when I use composition, and I don't want the 'composition member variable', in my example Rectangle rectShape, to ever be uninitialized?

Thanks for the input

Yes this is exactly what it means and is the way I usually do it as it is most flexible. If you didn't want a separate line declaring a rectShape just for passing into the ShapeArea ctor, you could always do this; you will need the correct ctor implementing for Rectangle taking two ints for its dimensions...


ShapeArea n(Rectangle(10,50));

Seriously, if a single, simple method would do easily, it is weird to add a whole another class for it.

Advertisement

Here is an example of how you might construct a composed object (C# sample but holds for other languages).

The factory constructs the items to be composed and then injects them into the entity.


public class Entity
{
	private readonly Point _location;
	private readonly HitPoints _hitPoints;
	
	public Entity(Point location, HitPoints hitPoints)
	{
		_location = location;
		_hitPoints = hitPoints;
	}
	
	......
}

public static EntityFactory
{
	public Entity Build(int initialHitPoints, Point location)
	{
		var hitPoints = 
			new HitPoints
			{
				InitialHitPoints = initialHitPoints,
				MaxHitPoints = initialHitPoints
			};
		
		return new Entity(location, hitPoints);
	}
}

There are many other ways to do this but you are right that you have to create the various components that you want to inject first. Using a factory means that you have one location for this composition instead of duplicate code around everywhere.

You might have a factory to construct a monster, another to build a weapon etc, all depends on the different types of game "thing" you can compose into your base "entity"

This topic is closed to new replies.

Advertisement