Skip to main content
GameDev.net gamedev.net
🔒 Locked

OOP Design Questions - instanceof (Java to C++)

Started by evilsanta Apr 25, 2009 at 4:11 PM 16 replies 3.2k views
Original Post
evilsanta
evilsanta
Hello all, I have some questions regarding the design of OOP programs. Right now my program is written in Java and uses the instanceof operator. I have heard that there is a lot of taboo about this, mostly that using it is generally bad design. I am now moving this program over to C++ and since C++ doesn't have instanceof I want to design the code better. Right now, I have a game that has a lot of different objects that are stored in my World class. I have a lot of different interfaces such as Logicable, Drawable, Collidable, etc. etc.. Each object is added to a specific list where the methods are then handled by my World class. If this is confusing allow me to show you guys an example of where I use instanceof...

	public static void addObject(Object o)
	{
		if(o instanceof Collidable)
		{
			staticCollisionList.add((Collidable)o);
		}
		if(o instanceof SortedDrawable)
		{
			sortedDrawableList.add((SortedDrawable)o);
		}
		if(o instanceof Logicable)
		{
			logicableList.add((Logicable)o);
		}
	}
Is this bad design? I am not sure how else I could achieve such ease in Java. Classes themselves are created, implement the interfaces they need, and then objects of the class can be added to the World using the above function and immediately run. Additionally if I want to achieve this in C++ how should I go about doing it? I am aware C++ uses multiple inheritance instead of interfaces. But what is the best method of storing and running objects which may have shared attributes. For example, if I had an Actor (updates, draws, and collides) and a Light (updates, draws), and a CollisionBox (draws, and collides) how should I keep track of and handle these? Thanks.
Antheus
Antheus
Quote:
Original post by evilsanta
Is this bad design?

IMHO, in most cases yes.

Quote:
I am not sure how else I could achieve such ease in Java.

The key question here is:
- Where does o come from?
- Does it need to be opaque type?

If it is an opaque type over whose creation you have no control, then this is often the only reasonable way to do it.

Quote:
Classes themselves are created, implement the interfaces they need, and then objects of the class can be added to the World using the above function and immediately run.

Additionally if I want to achieve this in C++ how should I go about doing it?


struct Foo {  Foo(World * world);};struct Collidable : public Foo {  Collidable(World * world) {    world->staticCollisionList.push_back(this);  }};struct World {  std::vector<Collidable *> staticCollisionList;  std::vector<SortedDrawable *> sortedDrawableList;  ...};


This works in Java as well. Let the object itself figure out where it wants to belong. Alternatively, if you can't know where it will belong at construction, add a virtual install(World *) method.

The constructor approach is my preferred one for component systems since it implicitly takes care of some life cycle issues.

This design does require 1:1 mapping between components (its base interface at least) and world (world needs to know all possible interfaces), but it also simplifies many aspects. In practice, there shouldn't be too many different interfaces (10,20), even if there are many more distinct implementations.
rip-off
rip-off
In general, the reason why instanceof() (and using other RTTI systems in program logic) are frowned upon is because they can be difficult to maintain. Every time you add a new class to the hierarchy, or change it (even if in this case, the problem is restricted to the upper levels), you now have to check all the places that might be affected. This code could be scattered all over your program, making it possible that you will forget one (or more!) area.

But in this particular case, you may be fairly sure you are not going to introduce more interfaces that the World class needs a special case for. In which case yours may be a brittle but acceptable solution.

C++ makes the burden slightly easier, because there are no interfaces, only classes, and they can have code. Example:
class Collidable;class SortedDrawable;class Logicable;class World{public:    void add(Logicable *);    void add(Collidable *);    void add(SortedDrawable *);};class Collidable{public:    Collidable(World &world)    {       world.add(this);    }    virtual ~Collidable(){}};class SortedDrawable{public:    SortedDrawable(World &world)    {       world.add(this);    }    virtual ~SortedDrawable(){}};class Logicable{public:    Logicable(World &world)    {       world.add(this);    }    virtual ~Logicable(){}};// I haven't had reason to use multiple inheritance recently// So I can't remember if this is the correct syntax.class GameObject:    public Logicable,    public Collidable,    public SortedDrawable{public:    GameObject(World &world)    :       Logicable(world),       Collidable(world),       SortedDrawable(world)    {    }};


That is just to show that you could do it. I wouldn't use this way. I think it would be better to use composition over inheritance for this. That is, an Object might have a Logicable, Collidable and SortedDrawable members, perhaps represented as boost::optional<> or a smart pointer of some description.

Another design issue with your code is the keyword "static" in addObject(). Shouldn't World be a class rather than a namespace, given that it has state?

Miscellaneous points:
Quote:

I am now moving this program over to C++ and since C++ doesn't have instanceof I want to design the code better.

C++ does have an equivalent of instanceof (at least for types with virtual functions). It is dynamic_cast<>. It also has typeid(), but that is a pain.

Quote:

I am aware C++ uses multiple inheritance instead of interfaces.

Not really. Most C++ code does not use multiple inheritance at all. C++ sometimes has totally different idioms for dealing with some issues.

For example, the Java interface "Comparable" is used for comparing objects by the standard library in Java. Some allow a custom comparator, but this still must derive from a specific base class. In C++, library functions like std::sort are templates, and they take a comparison functor. The functor defaults to being std::less, which is defined for types that have an overload of the operator <().

This is an example of how C++ achieves the same thing as Java without using inheritance at all.
dmatter
dmatter
Quote:
Original post by evilsanta
Is this bad design?
It is, it violates the Open-Closed Principle.


Quote:
Additionally if I want to achieve this in C++ how should I go about doing it?
Realistically there are a lot of ways to achieve this; you could use factory functions to create an entity however is necessary and register its components with the suitable lists:

entity * create_actor(/*params*/){    entity * e = new entity(/*params*/);    register_collidable(e, /*other params*/);    register_drawable  (e, /*other params*/);    register_logicable (e, /*other params*/);    return e;}entity * create_light(/*params*/){    entity * e = new entity(/*params*/);    register_drawable  (e, /*other params*/);    register_logicable (e, /*other params*/);    return e;}entity * create_box(/*params*/){    entity * e = new entity(/*params*/);    register_collidable(e, /*other params*/);    register_drawable  (e, /*other params*/);    return e;}void register_collidable(entity * e, /*params*/){    collidable_list.push_back( collidable(e, /*params*/) );}void register_drawable(entity * e, /*params*/){    drawable_list.push_back( drawable(e, /*params*/) );}void register_logicable(entity * e, /*params*/){    logicable_list.push_back( logicable(e, /*params*/) );}
evilsanta
evilsanta
Thanks for all the help everyone, this has hopefully set me on the correct path.

I did some reading on composition and I was wondering, if an Actor has a Collidable, Drawable, and a Logicable object, how would I cycle through all the possible Drawables stored in my World? Would I just have the Drawable constructor automatically add itself to the world?

Additionally in this case it seems like it would be best if an Actor extended Logicable, since an Actor could have many things that could occur during a logic update (such as motion) whereas another class such as Light, would not move but perhaps flicker during its logic update, however an Actor and a Light would both have a Drawable and Collidable object. I am not sure if this is correct reasoning, but I guess I should just get coding and see what works best.


[Edited by - evilsanta on April 25, 2009 5:16:29 PM]
Zahlman
Zahlman
Quote:
Original post by evilsanta
Thanks for all the help everyone, this has hopefully set me on the correct path.

I did some reading on composition and I was wondering, if an Actor has a Collidable, Drawable, and a Logicable object, how would I cycle through all the possible Drawables stored in my World?


Iterate ("cycle") through Actors, telling each to do something with its Drawable.

And could you please think of something nicer-sounding than "Logicable"? :) I would say "Updateable", but it doesn't *have* to be a -able, either.

Quote:
Additionally in this case it seems like it would be best if an Actor extended Logicable, since an Actor could have many things that could occur during a logic update (such as motion) whereas another class such as Light, would not move but perhaps flicker during its logic update, however an Actor and a Light would both have a Drawable and Collidable object. I am not sure if this is correct reasoning, but I guess I should just get coding and see what works best.


The amount of stuff that happens has nothing to do with it. What matters is the logic of the design: inheritance is meant to say the subclass instances are a special kind of the superclass (i.e., we follow the LSP: we could reasonably give a subclass instance to anything that expects an instance of the superclass), and composition is meant to say that the composer logically has the component as, well, a component.

Years of experience have shown that it is generally easier to misuse inheritance where composition should be used, than the other way around.
theOcelot
theOcelot
Or, have the Drawables add themselves to a list of Drawables, which is in an object contained by the world. Or, have Drawables only accessible by a factory/registry, which also "does stuff" to them, like drawing them. The latter approach also allows you to have multiple groups of Drawables at the same time, if for some reason you wanted that. You may find this thread interesting: Outboard Component-Based Entity System. Make sure you have some time before you start reading it, as it has five very long and dense pages.
evilsanta
evilsanta
Quote:
Original post by Zahlman
And could you please think of something nicer-sounding than "Logicable"? :) I would say "Updateable", but it doesn't *have* to be a -able, either.


My apologies, I just got used to having all interfaces ending in -able, and Logic sounds cooler. :)


Either way, I am attempting to build a small text based example of my game using composition. I am finding that using composition seems to take more time to do things. I am not quite sure I understand the advantages of this over multiple inheritance.

In my game, I need to be able to easily create an object that can collide. In my Java application I simply added all objects which were Collidable to a list of Collidable objects. So if I wanted to create a new class, for example Wall. I would simply implement Collidable and then it would instantly be able to collide with everything when passed to my World class. I am not sure how something similar can be achieved with composition, since if I have Wall contain a Collidable object how does the world know that the object added to it has a Collidable object?

Would I just do something like what Antheus suggested and have each individual component (Collidable, Drawable, etc.) add themselves to the appropriate World list? Or should I only be adding Wall to the World?

I guess I am just unsure why composition is superior to inheritance as it seems like it takes more work.


Edit: Woops, missed your post theOcelot I think that answers my question. Ill look into that article.

Additionally however, lets say I want classes to run during a logic update. I don't think an Actor should have a Logic object, because how would Actor customize the Logic objects update method? If I say, wanted to have an Actor to move left, Actor would normally just over-ride the update method and add in x--. But I am not sure how this would be done with composition, I am going to guess that this is a case where inheritance would be ok?
theOcelot
theOcelot
Yeah, the main sticking point for me of Component-based design is the whole logic/control thing. How does an object manipulate its owner and siblings in a clean way?

I'm convinced, though, that there is a good way to do it. The possibilities of a component-based controlling system are numerous and attractive. You could have an Actor controlled by either a hard-coded AI, a script, user input, or even a pre-recorded series of actions, as in a replay system, all with the same interface. You could easily drop any controller into, say, the main player Actor, or allow the user to take control of some other object, all almost painlessly.

I've toyed with various ideas involving callbacks, message queues, and stuff, but haven't figured out a good way to do it. Maybe someone here can tell us how they do it?
dmatter
dmatter
Quote:
Original post by evilsanta
My apologies, I just got used to having all interfaces ending in -able, and Logic sounds cooler. :)
What about Controllable? It has the benefits of being a well understood term as well as being a real word.

Quote:
I am finding that using composition seems to take more time to do things.
Take it as a good sign; composition is harder to achieve than inheritance but has the benefit of not promoting such a strong coupling.

Quote:
I am not sure how something similar can be achieved with composition, since if I have Wall contain a Collidable object how does the world know that the object added to it has a Collidable object?
If your entity aggregates components (as a vector of pointers to components for example) then you can just iterate that list and register components to wherever is appropriate.

Another option is to have components register themselves up on construction, a-la Antheus' example.

Alternatively you could have the entity register it's own components when that entity is added to the world (with composition the entity knows what components it has).

There is also the option of having the desired components registered, when an entity is contructed, by some sort of factory that creatures that entity, a-la my example above.

Finally you could construct the entity and then notify the component lists of this new entity, then will then create suitable components for that entity (or ignore the notification if a particular entity doesn't require such a component).

Quote:
Additionally however, lets say I want classes to run during a logic update. I don't think an Actor should have a Logic object, because how would Actor customize the Logic objects update method? If I say, wanted to have an Actor to move left, Actor would normally just over-ride the update method and add in x--. But I am not sure how this would be done with composition, I am going to guess that this is a case where inheritance would be ok?
You can specialise (inherit from) the logic component and override its update function - this lets you both customise the behaviour and maintain logic as a separate component.
evilsanta
evilsanta
Alright, I realize it has been a while but I have run into a wall with this composition method. I am having trouble getting children to interact using composition instead of inheritance.

I am currently adding a physics engine to my game.

Right now I have a PhysicsObject class, this contains a Draw object, it also contains a Body object (Body being a class in the physics engine). The Body and Draw objects add themselves to where they need to go. Body contains the information for location and orientation. However I need to Draw at the location of this child.

I am considering the Draw object to have a pointer to its parent so it can request the x and y location from the Body. I made the following pseudo code to help clarify.
class Entity	getx() - return 0	gety() - return 0	getrotation() - return 0class PhysicsObject extends Entity	Draw d	Body b	override getx() - return b.x	override gety() - return b.y	override getrotation() - return b.rotationclass Draw	Entity parent	draw() - call parent.getx()/parent.gety()


But I am not sure if this is best, since I might have to add more and more to the base class when extra functionality is required. Any advice would be appreciated.

[Edited by - evilsanta on May 7, 2009 11:17:28 PM]
Zahlman
Zahlman
Tell, don't ask. Also [google] "delegation" (maybe toss "OOP" or something in there too).

Have the Entity process requests to be drawn. Implement this by having the Entity tell the Body "give your position to this Draw object and tell me what render primitives it comes up with", giving it the Draw object that it composes. The Body responds by telling the passed-in Draw object "Give me a list of render primitives for drawing yourself at this location/orientation". The Draw object returns the information, which is then returned by the Body to the Entity, and then by the Entity to the main render function, which adds the render primitives to the render queue.
evilsanta
evilsanta
Thanks for the reply. I did some reading on delegation, but I think I am still a bit confused. Are you suggesting that the Draw object should not have to request the location from entity? But rather the entity should tell the Draw object where it is drawn?

Additionally I cannot pass the Body the Draw object since Body is not a class I designed myself, it is a class in the physics engine I am using. Perhaps I misinterpreted your point? Additionally I have a World class which handles the rendering. Currently Draw objects add themselves to a World object, then the draw method in Draw uses the parent pointer to request its x & y coordinates.


Thanks for putting up with me everyone, I am having quite a bit of difficulty wrapping my head around all of this. :)
Zahlman
Zahlman
Quote:
Original post by evilsanta
Are you suggesting that the Draw object should not have to request the location from entity? But rather the entity should tell the Draw object where it is drawn?


That's exactly it.

Quote:
Additionally I cannot pass the Body the Draw object since Body is not a class I designed myself, it is a class in the physics engine I am using.


So make a wrapper, and hide the fact that the engine expects you to use accessors. Or bite the bullet and use the accessors, and pass the information directly to Draw from Entity. Or bite the bullet and have the Entity pass Body to Draw, and have Draw use the accessors. I'll demonstrate the first approach.

Quote:
Perhaps I misinterpreted your point? Additionally I have a World class which handles the rendering. Currently Draw objects add themselves to a World object, then the draw method in Draw uses the parent pointer to request its x & y coordinates.


Look, I'll spell it out for you. (This is obviously very pseudo.)

class World {  RenderQueue rq;  Container<Entity> entities;  void drawEverything() {    for (Entity e: entities) { rq.add(e.primitives()); }    rq.sendEverythingToTheGPU();  }};class Entity {  BodyWrapper b;  Draw d;  Container<Primitive> primitives() { return b.drawWith(d); }};class BodyWrapper {  Body b;  Container<Primitive> drawWith(const Draw& d) { return d.drawAt(b.position, b.orientation); }};class Draw {  Container<Primitive> drawAt(Position position, Orientation orientation) {    // the real work happens here.  }};
evilsanta
evilsanta
Thanks, that pseudo code helped me quite a bit. Just one question though, this assumes that the type Primitive stores its own coordinates right? Then where your comment "// the real work happens here." creates and returns a new primitive at the specified location?

In my case I have an Image class which stores image data. Would it be best to just instead of returning a Primitive just draw the image? Or would it be better to give World an actual object to work with?
Zahlman
Zahlman
Quote:
Original post by evilsanta
Thanks, that pseudo code helped me quite a bit. Just one question though, this assumes that the type Primitive stores its own coordinates right? Then where your comment "// the real work happens here." creates and returns a new primitive at the specified location?


It returns a list of Primitives that represent how to draw the model. This is data, and doesn't need to be represented as a class; a struct will do. Format it however works best for feeding data to your graphics API.

Quote:
In my case I have an Image class which stores image data. Would it be best to just instead of returning a Primitive just draw the image? Or would it be better to give World an actual object to work with?


Please explain more about how your rendering works. I don't even know if this is a 2D or a 3D game right now.
evilsanta
evilsanta
Yeah, I just realized I hadn't mentioned what I'm working on. I am building a 2d game using opengl. Either way I have been fiddling around with this program for the better part of the day and I seem to have it working just fine.


I have a Primitive base class, which has methods for direct drawing and a width/height element. My Draw class holds a pointer to a Primitive (such as image data), this class has a method for drawing itself at coordinates (I attempted to create your pseudo code example as best I could). I successfully wrapped the body class into my own class only instead of having drawWith(Draw*) return a Primitive I just have it at void since Draw handles the direct drawing of its stored Primitive.

I think I finally understand the usefulness of this method... since I was able to easily create a new type of Primitive that could be handled and drawn by the Entity class (including Physics code) and I am not using multiple inheritance.
Zahlman
Zahlman
Sometimes you have to let people prove things to themselves. :)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.