Class organization question.

If you have any questions on programming, this is the place to ask them, whether you're a newbie or an experienced programmer. Discussion on programming in general is also welcome. We will help you with programming homework, but we will not do your work for you! Any porting requests must be made in Developmental Ideas.
Post Reply
User avatar
GyroVorbis
Elysian Shadows Developer
Elysian Shadows Developer
Posts: 1874
https://www.artistsworkshop.eu/meble-kuchenne-na-wymiar-warszawa-gdzie-zamowic/
Joined: Mon Mar 22, 2004 4:55 pm
Location: #%^&*!!!11one Super Sonic
Has thanked: 80 times
Been thanked: 62 times
Contact:

Class organization question.

Post by GyroVorbis »

I usually try to keep my code as organized as I possibly can. I notice that I have more fun when trying to work on my projects, and I have a large motivation increase when I feel like everything is neat.

The project that I'm working on right now is the largest scale thing I've ever done in my life. The conventions that I started out using really wound up becoming a pain in the ass when the project got bigger. I switched from C to C++ because I thought the organization and OO approach would be nicer (and it was for the most part).

My problem is class dependencies. I have several main classes in the game that are all dependent upon each other. I have a Level class that requires a Player class for several of its functions, a System class that requires the Level, Player requires System, Player requires Input, and so on. It's like a spiderweb of class dependencies.

When I was doing the project in C, I had one global object for each. I had a global Player, System, Level, and Input object. All of their members were public. So any time, in any class, I could access another class. This worked nicely, but I was sure it wasn't the best practice that I could be using.

When I switched to C++, I started trying to pass references to these member functions. For example, the Collision function in the Player class would take a reference of the Level so that it can access the map to see if the player is hitting a solid. This worked out for a few weeks, but I literally wanted to vomit when the game got larger. I was spending sooo much time just worrying about passing things around the program, that I completely scrapped that idea. Some classes and functions require a reference of both a Level and a System. Some need a Level and Input. I just got TIRED of it.

What I'm doing now is ultimately like I began with in C:

Code: Select all

class Example {
public:
	static Example &GetInstance() {
        static Example instance;
        return instance;
    }
private:
	Example();
};
Now if I need to use Example in a function somewhere, I do this:

Code: Select all

Example &Obj = Example::GetInstance();
This is working out for me nicely, but I'm not sure if this is the best solution. I use this quite a bit. Many times I'm getting multiple global instances (Font, Level, Player, System) per function in this manner. I don't know if there would be a speed penalty or if this is efficient (let alone a good practice).

I am extremely interested in hearing your solutions to this sort of extreme class dependency. Sure, I could probably make things a little less dependent upon each other, but it's a major pain in the ass. Ultimately I will still always have classes that require other classes. I'd like to find a solution that is safe, easy to use, and doesn't require lots of extra work to implement.
Sir Savant
Somewhat Dumb Knight
Posts: 3653
Joined: Tue Oct 12, 2004 2:26 pm
Has thanked: 0
Been thanked: 0

Re: Class organization question.

Post by Sir Savant »

Not for nothing, but you could try making a diagram for your class dependencies.

I actually try and see what the basic functionality of something is. I have no idea what the code for what you are doing looks like, so I can't suggest something specific, but if I can I usually abstract functionality so that the basic objects are merely shells and other objects extend them (by filling in code). As the organization gets deeper, other things extend more global objects, refining the objects in question.

I think it's a question of really figuring out what SHOULD go where as opposed to whats convenient at the time of coding. I don't know specifically what your classes are doing, but perhaps you are reusing code where it really shouldn't be reused and should be reimplemented. It's nice to use an IDE that actually has this implemented (the closest I can figure might be BlueJ for Java, but idk the C++ equivalent), where all the dependencies and calls are shown in a nice diagram.

I'm rambling with no real purpose so I'll stop here.
User avatar
showka
DCEmu Freak
DCEmu Freak
Posts: 95
Joined: Fri Nov 23, 2001 12:01 pm
Location: Border Town
Has thanked: 0
Been thanked: 0
Contact:

Re: Class organization question.

Post by showka »

I understand your larger problem with being weary of all the relations between classes, because I experience it myself. I don't know the solution, and have realized lately my main fault as a programmer is that I'm prone to over-engineer my designs and make them more abstract and "re-usable" than usable, so maybe you shouldn't listen to me.

I find when I used mostly just old C, I never would have done something like constantly call a function to get a global pointer before calling another function. With C, I really couldn't help but focus on efficiency to a fault, and would obsess over dumb things like the size of a variable to use for a structure that didn't matter that much anyway.

With C++, I feel like the language instead causes me to focus on the abstract "pattern" of how things are working, and many times seems to hide major inefficiencies that would've stuck out like a sore thumb had I been using normal C. As a result, I spend too much time obsessing over how usable my classes will be in the future and whether I should make some base object to pass to a function instead. For example, when you said you're player has a function that accepts a level to check for a collision, I thought "The level should accept a player object, or better yet! An base type of player, which could be an enemy, or anything at all, because this would be more usable lah blah blah.." That should show how warped my thought process has become (what point is modifying all of that if you might not need it?).

But I know you shouldn't worry about the speed penalty from getting the global pointer.

I've been told that the compiler will automatically inline most small bits of code, but you might want to put an explicit "inline" on the "GetInstance()" function. As I understand it at that point all of those "GetInstance" calls are just as fast as a macro and you shouldn't lose much speed at all.

By the way, there's no guarantee as to when that static global will be initialized, so you'll want to make sure you're not doing anything that's order dependent in the constructor.

What I normally do is include a pointer to a global instance in the class as a private member. Then in the cpp file, I'll have a line such as "Example * Example::instancePtr == nullptr;" to make sure it starts off as null (or NULL). The GetInstance() function then initializes the instancePtr if it's null, then returns it.

If the a class I'm using this pattern with has a complicated start up, I'll have two functions. The first one initializes the global instance pointer if it's null using however many initialization parameters are needed, and throws an assert if it's not null. The second one just returns the instance, regardless of it's state (so sometimes it returns null).

I'm using this pattern on a class object in my latest project which is essentially the "brain" of a game. The start up code that creates it passes several parameters, but all of the player, enemy and other "game" types just use the simple "GetInstance()" function to return the instance of the game. They then use that instance of the game class to get pointers to other objects, such as an object which manages hit boxes.
dospro
Insane DCEmu
Insane DCEmu
Posts: 147
Joined: Thu Dec 30, 2004 7:12 pm
Has thanked: 0
Been thanked: 0
Contact:

Re: Class organization question.

Post by dospro »

What i do in this cases generally is to have a pointer of the needed classes in each clasee an a member like: GetLevelObject(), GetSomething().
Maybe it's not strict OOP but it works nicely and make life much easier.
Check the BioGB emulator any help me get it much better!!
http://www.geocities.com/instructivus
User avatar
henzenmann
Insane DCEmu
Insane DCEmu
Posts: 186
Joined: Wed Jul 12, 2006 4:58 pm
Has thanked: 0
Been thanked: 0
Contact:

Re: Class organization question.

Post by henzenmann »

showka wrote: By the way, there's no guarantee as to when that static global will be initialized, so you'll want to make sure you're not doing anything that's order dependent in the constructor.
It is not a static global, but a static local :) This type of construct is known to be a work-around for that static dependency problem.
Ex-Cyber
DCEmu User with No Life
DCEmu User with No Life
Posts: 3641
Joined: Sat Feb 16, 2002 1:55 pm
Has thanked: 0
Been thanked: 0

Re: Class organization question.

Post by Ex-Cyber »

Unfortunately I haven't learned C++, so I can't speak to issues specific to it.
GyroVorbis wrote:My problem is class dependencies. I have several main classes in the game that are all dependent upon each other. I have a Level class that requires a Player class for several of its functions, a System class that requires the Level, Player requires System, Player requires Input, and so on. It's like a spiderweb of class dependencies.
I guess the question you have to ask yourself is: which of those dependencies are actually necessary, and which are just quirks of the way that you put the program together? If a dependency is necessary, then there's not much you can do about it - it's just the nature of the problem you're solving, and attempting to "fix" it will just give you the same dependency in a different arrangement. Think about which class should have the authority or responsibility (there's probably a better term, but I can't think of it) to maintain a given element of the program's state, and make that state and its directly related methods part of that class. I know it's easier said than done, but it's just part of the process.
"You know, I have a great, wonderful, really original method of teaching antitrust law, and it kept 80 percent of the students awake. They learned things. It was fabulous." -- Justice Stephen Breyer
User avatar
showka
DCEmu Freak
DCEmu Freak
Posts: 95
Joined: Fri Nov 23, 2001 12:01 pm
Location: Border Town
Has thanked: 0
Been thanked: 0
Contact:

Re: Class organization question.

Post by showka »

henzenmann wrote:
showka wrote: By the way, there's no guarantee as to when that static global will be initialized, so you'll want to make sure you're not doing anything that's order dependent in the constructor.
It is not a static global, but a static local :) This type of construct is known to be a work-around for that static dependency problem.
Thanks for the correction. Learn something new every day, etc etc.

The only issue I'd have with using the static is the GetInstance() call will not get compiled inline, so there will be the cost of a function call every time you retrieve it. Although damn, I may be mistaken again - is it even guaranteed that a static class member such as a pointer will be initialized if it's in a separately compiled translation unit, and the only function call made to the class before the static member is needed is inline?

For some reason I thought static class variables were different that other static objects but now that I think about it that may be wrong.
User avatar
henzenmann
Insane DCEmu
Insane DCEmu
Posts: 186
Joined: Wed Jul 12, 2006 4:58 pm
Has thanked: 0
Been thanked: 0
Contact:

Re: Class organization question.

Post by henzenmann »

showka wrote:Although damn, I may be mistaken again - is it even guaranteed that a static class member such as a pointer will be initialized if it's in a separately compiled translation unit, and the only function call made to the class before the static member is needed is inline?
No, the class member will be initialized (nullptr). In fact, the method you described (checking if pointer is null etc.) is pretty similar to what the compiler does with the local static.

Edit:
The function can be inlined, but of course you still have a branch per call to "GetInstance()" that you would not have with a global pointer. It should not make too much of a differnce though.
User avatar
toastman
Iron Fist of Justice
Iron Fist of Justice
Posts: 4933
Joined: Sat Nov 10, 2001 3:08 am
Location: New Orleans
Has thanked: 0
Been thanked: 0
Contact:

Re: Class organization question.

Post by toastman »

GyroVorbis wrote:I usually try to keep my code as organized as I possibly can. I notice that I have more fun when trying to work on my projects, and I have a large motivation increase when I feel like everything is neat.

The project that I'm working on right now is the largest scale thing I've ever done in my life. The conventions that I started out using really wound up becoming a pain in the ass when the project got bigger. I switched from C to C++ because I thought the organization and OO approach would be nicer (and it was for the most part).

My problem is class dependencies. I have several main classes in the game that are all dependent upon each other. I have a Level class that requires a Player class for several of its functions, a System class that requires the Level, Player requires System, Player requires Input, and so on. It's like a spiderweb of class dependencies.

When I was doing the project in C, I had one global object for each. I had a global Player, System, Level, and Input object. All of their members were public. So any time, in any class, I could access another class. This worked nicely, but I was sure it wasn't the best practice that I could be using.

When I switched to C++, I started trying to pass references to these member functions. For example, the Collision function in the Player class would take a reference of the Level so that it can access the map to see if the player is hitting a solid. This worked out for a few weeks, but I literally wanted to vomit when the game got larger. I was spending sooo much time just worrying about passing things around the program, that I completely scrapped that idea. Some classes and functions require a reference of both a Level and a System. Some need a Level and Input. I just got TIRED of it.

What I'm doing now is ultimately like I began with in C:

Code: Select all

class Example {
public:
	static Example &GetInstance() {
        static Example instance;
        return instance;
    }
private:
	Example();
};
Now if I need to use Example in a function somewhere, I do this:

Code: Select all

Example &Obj = Example::GetInstance();
This is working out for me nicely, but I'm not sure if this is the best solution. I use this quite a bit. Many times I'm getting multiple global instances (Font, Level, Player, System) per function in this manner. I don't know if there would be a speed penalty or if this is efficient (let alone a good practice).

I am extremely interested in hearing your solutions to this sort of extreme class dependency. Sure, I could probably make things a little less dependent upon each other, but it's a major pain in the ass. Ultimately I will still always have classes that require other classes. I'd like to find a solution that is safe, easy to use, and doesn't require lots of extra work to implement.
I don't know if you've actually studied it, but you've hit upon the [link="http://en.wikipedia.org/wiki/Singleton_pattern"]Singleton class pattern[/link].

Basically there are only two relationships in OO: "is-a" and "has-a". Is-a relationships imply inheritance and has-a relationships imply containment. Ultimately, your classes should make a sentence: <Class1> is-a/has-a <Class2>.

Ultimately you should ask yourself does the Level really work on the player, or does the player work on the level, or do the player and level need to meet in some arbitrary location and resolve their differences.

I would do one of two things.
I would remove every inter-class dependency I could. I think player colliding with a wall was one. I would extrapolate from that that a player hitting a wall was just a special case of a player hitting an object (as such, this could be used for hitting enemies and projectiles as well) and create a function that took two shapes and returned whether of not they intersect.
Something like:

Code: Select all

doesIntersect(player.getShape(), level.getCorridorSection(player.getLocation()))
The other thing (and I've had to do this more with C# where everything is a class) I would do is to keep a reference to the other object in the class. Then I would just pass in the other object in the constructor, that way I'm not passing it around like the village bike.

Code: Select all

Level *level = new level();
Player *player = new player(level);
No signature.
Post Reply