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

Class design/implementation

Started by Myra Mains Feb 3, 2005 at 3:41 PM 10 replies 1.8k views
Original Post
Myra Mains
Myra Mains
Hi All, I am looking for some design/implementation type help. I am building a game engine in c++ and I have a bit of a problem. So far everything works but I don’t like the way I am going about this. So I am looking to you all for some help if you can. I have three classes lets call them A, B, and C. A needs to call functions in B and C. B needs to call functions in A. And C needs to call functions in A. Right now I have just given each of them a pointer to the others that I assign after all three are created. But I don’t think that this is the right way to do this. I am not really having any problems it just doesn’t feel right to me. Thanks.
Amma
Amma
Well my game is compiling so I'll just answer this one ...


Seems like your A,B & C classes should be singletons, so ...


(a.hpp)
class A : public CSingleton {
public:
void TestA() {}
};

(b.hpp)
class B : public CSingleton {
public:
void TestB() {}
};

(c.hpp)
class C : public CSingleton {
public:
void TestC() {}
};



(a.cpp)
#include "a.h"

A::TestA()
{
B::Instance().TestB(); /// <- Here you're calling a function in 'B'!
}


voila! :)
-----------------------------Amma
maximAL
maximAL
Quote:
Original post by Myra MainsI have three classes lets call them A, B, and C. A needs to call functions in B and C. B needs to call functions in A. And C needs to call functions in A. Right now I have just given each of them a pointer to the others that I assign after all three are created. But I don’t think that this is the right way to do this.


well, this sounds like really bad design, because you got too many dependencies between classes.
if possible, you should put the communication between classes into an extra mediator-class (->look for mediator pattern).
------------------------------------------------------------Jawohl, Herr Oberst!
Myra Mains
Myra Mains
Quote:
Original post by Amma
Well my game is compiling so I'll just answer this one ...
Seems like your A,B & C classes should be singletons, so ...
(a.hpp)
class A : public CSingleton {
public:
void TestA() {}
};
(b.hpp)
class B : public CSingleton {
public:
void TestB() {}
};
(c.hpp)
class C : public CSingleton {
public:
void TestC() {}
};
(a.cpp)
#include "a.h"
A::TestA()
{
B::Instance().TestB(); /// <- Here you're calling a function in 'B'!
}
voila! :)


This is almost exactly what I am doing now which I dont like because every class depends on every other class.
Myra Mains
Myra Mains
Quote:
Original post by maximAL
well, this sounds like really bad design, because you got too many dependencies between classes.


Exactly! I didn't like it but I didnot know of any other way to go about it.

Quote:
Original post by maximAL
if possible, you should put the communication between classes into an extra mediator-class (->look for mediator pattern).


After a few minutes on google I feel that this is exactly what I was looking for. I just didnt know what to search on. Thank you!
maximAL
maximAL
btw, i'd recommend this book if you run into problems like this from time to time: http://www.gamedev.net/columns/books/bookdetails.asp?productid=18
;)
------------------------------------------------------------Jawohl, Herr Oberst!
Myra Mains
Myra Mains
Quote:
Original post by maximAL
btw, i'd recommend this book if you run into problems like this from time to time: http://www.gamedev.net/columns/books/bookdetails.asp?productid=18
;)


I ordered it based on your recommendation and reviews from other GameDev members. Thanks!
antareus
antareus
What classes are they? Knowing the names of them will help us better determine if the coupling is unnecessary.

Quote:
Seems like your A,B & C classes should be singletons, so ...

Wrong on multiple levels. The OP does not need a single instance of a class, nor does he need global accessibility.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
David ONeil
David ONeil
You may find that your original design IS what you needed. A well designed engine is in many ways, if not all, a mediator design. The example given in the GOF book describes using a mediator for a dialog box so that each of the widgets doesn't have to know about the other widgets. They just need to know about the mediator. From your description, "lets call them A, B, and C. A needs to call functions in B and C. B needs to call functions in A. And C needs to call functions in A". This is the mediator pattern, with A being the mediator, and B and C being the widgets that have to call back into A. It sounds like you did it right to me. Sometimes a class HAS TO KNOW about other classes, and there is no other way to do that than through pointers or other esoteric methods. A pointer is often the simplest method, and, as they say, the simpler, the better.

David
Myra Mains
Myra Mains
Quote:
Original post by antareus
What classes are they? Knowing the names of them will help us better determine if the coupling is unnecessary.


I guess you are right. I originally thought it would make things too complex to explain but guess it may be necessary.

Class A is my UI.
Class B is my game object controller
Class C is my multiplayer class

The UI is the complex one as different pieces of the UI need to work with many different combinations of all three.

In my UI the chat window needs to both send and recieve from the multiplayer class while health and status windows need to get info from the game objects they represent. The player action menus need to send info to both the game object and the multiplayer code and of course the multiplayer codes needs to tell the game objects to do stuff.
hplus0603
hplus0603
Use the interface-and-implementation pattern.

For each "thing" that can be called from some other "thing," define a pure abstract base class. That might be class I_A, I_B and I_C in your code.

For each concrete class, derive from the interface or interfaces it implements. Manufacture this instance through a factory function, and return it by interface. The concrete class (class A : public I_A) is actually declared in the .cpp file of the factory function, and not publicly visible.

If I_A needs to be configured with I_B and I_C, then forward declare those interfaces and have a configuration phase of I_A. It might look something like:

// I_A.h:class I_B;class I_C;class I_A {  public:    virtual void configure( I_B * b, I_C * c ) = 0;    virtual void doStuffA() = 0;};I_A * MakeA();// I_B.h:class I_A;class I_B {  public:    virtual void configure( I_A * a ) = 0;    virtual void doStuffB() = 0;};I_B * MakeB();// I_C.h:class I_A;class I_C {  public:    virtual void configure( I_A * a ) = 0;    virtual void doStuffC() = 0;};// One or more implementation files:#include "I_A.h"#include "I_B.h"#include "I_C.h"class A : public I_A {  public:    A() : b_( 0 ), c_( 0 ) {}    void configure( I_B * b, I_C * c ) { b_ = b; c_ = c; }    void doStuffA() { assert( b_ && c_ ); }};I_A * MakeA() {  return new A;}... etc for B and C ...// main.cpp:int main() {  I_A * a = MakeA();  I_B * b = MakeB();  I_C * c = MakeC();  a->configure( b, c );  b->configure( a );  c->configure( a );  .. do stuff ..}

enum Bool { True, False, FileNotFound };
David ONeil
David ONeil
Quote:
Original post by hplus0603
Use the interface-and-implementation pattern...


Why would you use something this complicated? It seems to me that the OP's original question can most simply be accomplished by not using Singletons, and passing the A 'this' pointer into B and C and registering it as their parent in B and C, while storing B and C's pointers in A. Much, much simpler, or so it seems. It is bad enough that they have to be coupled - why put another layer of indirection in with the coupling? In fact, if B and C rely upon the existence of A in their own ctors, a quick glance indicates that the pattern you indicated will not work, unless, of course, you actually do pass B and C the pointer to A to begin with.

Curious Dave

Topic Locked

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

Sign in to reply to this topic.