Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411587 Posts in 69386 Topics- by 58445 Members - Latest Member: Mansreign

May 06, 2024, 08:23:18 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)A question about writing managers
Pages: [1] 2
Print
Author Topic: A question about writing managers  (Read 5353 times)
Loren Schmidt
Level 10
*****



View Profile WWW
« on: June 13, 2009, 10:41:23 AM »

I will preface this by saying that I started game development as an artist, and am a novice at best when it comes to code.

There are a few different classes such as GUIManager, Input etc. that are accessed widely by different parts of a game. There is only a single instance of each of these classes, so they are currently singletons.

So whenever one of them is accessed, something like this occurs:
Code:
Draw.getInstance().method(arguments)

getInstance() seems to appear all over the place, and it makes the code less readable. So what if all such manager classes had only static variables and methods? Then use of such classes would reduce to:
Code:
Draw.method(arguments)

I'm lead to understand that this isn't really strictly proper- what happens if one of these static methods is called and the class hasn't been initialized yet? Is it okay to just assume that that will break the program and trust myself to have initialized all classes properly? I'm trying to find some kind of middle ground here between totally safe, heavy code and readability / cleanliness.
Logged
MrChocolateBear
Level 1
*



View Profile WWW
« Reply #1 on: June 13, 2009, 10:54:51 AM »

...
So whenever one of them is accessed, something like this occurs:
Code:
Draw.getInstance().method(arguments)

getInstance() seems to appear all over the place, and it makes the code less readable. So what if all such manager classes had only static variables and methods? Then use of such classes would reduce to:
Code:
Draw.method(arguments)
...

Why don't you use a variable to store the instance, like so:
Code:
render = Draw.getInstance()
render.method(arguements)
That way you can still the singleton design pattern and maintain code cleanliness/readability.
Logged

Ivan
Owl Country
Level 10
*


alright, let's see what we can see


View Profile
« Reply #2 on: June 13, 2009, 11:04:45 AM »

I mean, there's no reason why you can't wrap every single method in a static accessor that will get the instance within it. But really, it's a pain in the ass and i personally don't think that "getInstance()" makes code unreadable.
Logged

http://polycode.org/ - Free, cross-platform, open-source engine.
Glaiel-Gamer
Guest
« Reply #3 on: June 13, 2009, 11:08:45 AM »

#define Draw Draw.getInstance()
Logged
MrChocolateBear
Level 1
*



View Profile WWW
« Reply #4 on: June 13, 2009, 11:39:04 AM »

#define Draw Draw.getInstance()
Not part of my personal paradigm, but it is a valid solution. This could be the easiest solution for you, Sparky, because it requires the least amount of code changing.
Logged

Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #5 on: June 13, 2009, 12:05:39 PM »

I'm lead to understand that this isn't really strictly proper- what happens if one of these static methods is called and the class hasn't been initialized yet? Is it okay to just assume that that will break the program and trust myself to have initialized all classes properly? I'm trying to find some kind of middle ground here between totally safe, heavy code and readability / cleanliness.

Many languages provide facilities for "static initializers," blocks of code that run before your main executes and ensure initialization of static variables.

I don't know what language you're using (it looks like either Java or C#), but I know that C#, Java, and Ada all support this capability.  C++ can fake it using a local object.

I believe it looks something like this in Java:

Code:
public class StaticStuff
{
    private static String s1;
    private static Vector<Integer> v1;

    // Static initializer:
    static
    {
        s1 = "I could have done this directly";
        v1 = new Vector<Integer>();

        // Put some data in v1.
    }
}

I believe this is done in C# with a static constructor:

Code:
public class StaticStuff
{
    static StaticStuff()
    {
        // Init statics.
    }
}

Ada uses package initializer blocks:

Code:
package body Static_Stuff is
    Ints: Integers.List;

-- Package init code.
begin
    Ints.Append(10);
    Ints.Append(15);
end Static_Stuff;

You can fake it in C++ like so:

Code:
// StaticStuff.h
#ifndef STATIC_STUFF_H
#define STATIC_STUFF_H

namespace StaticStuff
{
    void DoWhatever();
    void DoSomethingElse();
}

#endif

// StaticStuff.cpp
#include "StaticStuff.h"
#include <map>

using std::map;

namespace
{
    // Local data.
    map<int, int> sparse_array;

    struct StaticInit
    {
        StaticInit()
        {
            sparse_array[5] = 7;
            sparse_array[7] = 10;
        }
    } static_init;
}

void StaticStuff::DoWhatever()
{
    // whatever
}
//etc...

Personally, I've never understood the need for the singleton pattern, at least in C++.  If you only want one of an item to exist, it's far simpler to just use a namespace with some translation unit local data.  I believe the singleton notion arose from pure object oriented languages like Smalltalk, where you had little to no support for free-standing functions and data.  The procedural capabilities of hybrid languages like C++ and Ada provide a far superior alternative, in my opinion.
Logged



What would John Carmack do?
MrChocolateBear
Level 1
*



View Profile WWW
« Reply #6 on: June 13, 2009, 12:45:15 PM »

Personally, I've never understood the need for the singleton pattern, at least in C++.  If you only want one of an item to exist, it's far simpler to just use a namespace with some translation unit local data.  I believe the singleton notion arose from pure object oriented languages like Smalltalk, where you had little to no support for free-standing functions and data.  The procedural capabilities of hybrid languages like C++ and Ada provide a far superior alternative, in my opinion.

Perhaps I overlooked something, but could you go over the benefits your proposed method over the singleton design pattern? From what I gather, the main benefit is creating objects before program execution. The problem I see is that you have no way to uninitialize information after execution begins, or perhaps I am mistaken?
Logged

Glaiel-Gamer
Guest
« Reply #7 on: June 13, 2009, 01:09:16 PM »

I have a lot of singletons in my game that are extensions of another class, so there's one reason
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #8 on: June 13, 2009, 01:20:50 PM »

Perhaps I overlooked something, but could you go over the benefits your proposed method over the singleton design pattern? From what I gather, the main benefit is creating objects before program execution. The problem I see is that you have no way to uninitialize information after execution begins, or perhaps I am mistaken?

Most design patterns exist to provide some kind of functionality that is missing from a language.  Therefore, most design patterns don't make sense in all languages, because some languages actually have the feature that the design pattern simulates.  In those cases, it's usually overly complex and counter-productive to use a design pattern when you can just use the native language features.

As a few examples, the fairly well known visitor pattern is a way to simulate something called a "multimethod."  If your language directly supports multimethods, (I believe Lisp does, I don't know of any others) it's pretty to silly to fake it using the visitor pattern when you can just do it directly.

Likewise, the observer pattern is a simulation of message forwarding.  Objective-C supports message forwarding, so using the observer pattern in Objective-C is silly.

In the vast majority of cases that I've seen, singletons provide the exact same functionality and lifetime as a simple namespace with local data.  The singleton pattern is (in most cases) a way to simulate the presence of free functions and data in a language that forces everything into a class.  C++, Ada, and Objective-C (among others) don't force you to put everything into a class, so using singletons in those languages is typically a waste of effort.

Now sometimes you do need additional control over creation and destruction of resources, and it's not hard to add Initialize() and Destroy() functions to a namespace or package or whatever to provide those facilities.  With most singletons, you end up having to make those calls explicitly anyway, so you really don't lose anything.

It comes down to a variation on Occam's Razor, the simplest implementation is typically the best.  A lot of singleton advocates seem to suffer from what some programmers call the "Object Oriented Religion," a fanatical belief that anything and everything should be in a class.  In languages like Java and C# you don't have choice, so singletons have a place there.  In hybrid languages like C++ and Ada you have options, and the object oriented one is often not the best way to go about doing things.

Now I'm not saying that singletons have no place at all in C++/Ada/et al..., it's just that you need to evaluate the situation and decide if that's what you really need.  I've been a C++ programmer for 8 years and I've never come across a case that needed a singleton in all that time.  There were some situations where I actually did use one, but then later I realized that I was being an idiot and I just changed them to simple namespaces.  My code became much cleaner and easier to manage as a result.
Logged



What would John Carmack do?
MrChocolateBear
Level 1
*



View Profile WWW
« Reply #9 on: June 13, 2009, 01:39:24 PM »

Average Software, thanks for the quick reply. After reading what you had to say, I feel our solutions are more similar in practice than I originally thought. Though I don't consider myself part of the "Object Oriented Religion," I will admit to taking a few sips from the "Design Pattern" Kool aid! Beer!
Logged

BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #10 on: June 13, 2009, 03:04:42 PM »

Singletons are not entirely pointless in C++, despite the existence of free methods and static data blocks.

Firstly, putting things together in a class is better encapsulation. Handy if you refactor to avoid singletons (which you should consider if you find yourself over using them). Or simply for data hiding.

Secondly, you cannot initialize everything using static blocks. Try initializing a vector<int> with the elements 1,2,3 in it. Worse, I cannot give any guarantees about the order of different static variables, meaning any complicated structures are impossible. (note I don't think any of this applies to Ada).

I would just leave in all the .instance() calls. They are bit ugly, but you won't forget it's a singleton, and the consequences this entails. Consider eliminating all static/global behaviour via dependency injection, if it's getting out of hand.
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #11 on: June 13, 2009, 03:41:38 PM »

Secondly, you cannot initialize everything using static blocks. Try initializing a vector<int> with the elements 1,2,3 in it.

Sure you can, here's one way:

Code:
namespace
{
    vector<int> data;

    struct StaticInit
    {
        StaticInit()
        {
            data.push_back(1);
            data.push_back(2);
            data.push_back(3);
        }
    } static_init;
}

Here's another:

Code:
namespace
{
    vector<int> vector_3()
    {
        vector<int> v;

        v.push_back(1);
        v.push_back(2);
        v.push_back(3);

        return v;
    }

    vector<int> data = vector_3();
}

Even better, C++09 allows this:

Code:
namespace
{
    vector<int> data = {1, 2, 3};
}

Worse, I cannot give any guarantees about the order of different static variables, meaning any complicated structures are impossible. (note I don't think any of this applies to Ada).

Yes, static initialization order can be a problem, and yes, Ada is the only language I know of that actually provides a means for dealing with that.  Singletons have the same problem though, they just move it somewhere else.  It is entirely possible for the creation of a singleton to depend on the creation of another singleton, so I believe this a moot point.
Logged



What would John Carmack do?
Snakey
Level 2
**


View Profile WWW
« Reply #12 on: June 13, 2009, 04:59:29 PM »

The only time I can think of a valid solution to use some form of singleton is when you don't actually know what the singleton is going to contain. That is, you may have three different renderer classes that all share a common interface.
Logged

I like turtles.
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #13 on: June 13, 2009, 05:33:39 PM »

Sure you can, here's one way:
The first way is declaring a singleton, just a rudimentary one. You're right about as a function return value, though, so I must have cotten mixed up. I thought that wouldn't compile.

Yes, static initialization order can be a problem, and yes, Ada is the only language I know of that actually provides a means for dealing with that.  Singletons have the same problem though, they just move it somewhere else.  It is entirely possible for the creation of a singleton to depend on the creation of another singleton, so I believe this a moot point.
Not moot. Singletons can be lazily initialized when requested, rather than during static initialization. Assuming you don't have any cyclic dependancy, this means you are entirely free to have singletons depend upon each other, as if A requires B, then on A's first use of B, B will get fully instantiated before A continues.
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #14 on: June 13, 2009, 05:50:55 PM »

The first way is declaring a singleton, just a rudimentary one.

No, the first way is abusing top level objects to perform initialization of other objects.  Nothing about the class is a singleton, I can create as many as I want, there's just no reason for doing so.  The fact that only one instance exists does not make it a singleton.  If my program only contained one instance of map<string, float>, would you call map<string, float> a singleton?

Not moot. Singletons can be lazily initialized when requested, rather than during static initialization. Assuming you don't have any cyclic dependancy, this means you are entirely free to have singletons depend upon each other, as if A requires B, then on A's first use of B, B will get fully instantiated before A continues.

Lazily instantiated objects fall outside of the realm of my point.  My argument is that most singletons have (effectively) program-wide lifespan, which is in practice no different than a simple namespace/data combination.  Singletons with variable lifespans are a slightly different beast.
Logged



What would John Carmack do?
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #15 on: June 13, 2009, 06:02:07 PM »

Firstly, putting things together in a class is better encapsulation. Handy if you refactor to avoid singletons (which you should consider if you find yourself over using them). Or simply for data hiding.

Sorry to go back for this, but I just noticed it and had to call it out.

Implementing singletons as namespace/data units is actually better encapsulation that a class, because it keeps ALL the detail of the internal data out of the header.  This not only hides the data, but also makes the header immune to changes to the internal data structures.  Consider:

Code:
class SomeSingleton
{
public:
    static SomeSingleton &Instance();

    void DoIt();
    int GetIt() const;
    void SetIt(int x);

private:
    int some_data;
    int other_data;

    static SomeSingleton *the_instance;
};

Now the namespace version:

Code:
namespace SomeSingleton
{
    void DoIt();
    int GetIt();
    void SetIt(int x);
}

This approach not only makes the data private, it also hides its structure altogether.  It also doesn't force clients to recompile when you add a piece of data to the singleton, since the data is encapsulated in the .cpp file.
Logged



What would John Carmack do?
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #16 on: June 14, 2009, 01:43:13 AM »

Lazily instantiated objects fall outside of the realm of my point.  My argument is that most singletons have (effectively) program-wide lifespan, which is in practice no different than a simple namespace/data combination.  Singletons with variable lifespans are a slightly different beast.
These are straw man singletons you are apparently talking about. Those do suck. As a simple transformation can make any singleton lazily instantiated, it's rubbish to say that most singletons are not like this. Many people would argue this is the whole point of them, as then you can have have polymorphic singletons where the implementing class varies according to, say, some config file.

Otherwise, yeah, I see now. Data hiding could be achieved with standard idioms for C++ classes, but there's not so much point for singletons.
« Last Edit: June 14, 2009, 02:46:02 AM by BorisTheBrave » Logged
bateleur
Level 10
*****



View Profile
« Reply #17 on: June 15, 2009, 02:54:58 AM »

Writing Flash stuff I prefer to cache local copies of important singletons as instance properties. This has the key benefit that if I later decide I want them not to be singletons anymore it's very easy to make the change.

The downside is that it makes my instances slightly larger, but that's not going to be relevant unless I'm making tens of thousands of instances.
Logged

Impossible
Level 3
***



View Profile
« Reply #18 on: June 15, 2009, 12:57:35 PM »

I'm not a huge fan of singleton, but it has it's place. The thing is, it looks like people are wrapping most (if not all?) of their major systems in some singleton class.  I don't really see why input or rendering need to be singletons?  They make sense as singletons because you probably only want to have one instance of your rendering or input manager classes, but it doesn't make sense for rendering or input to be globally accessible.
Logged
Snakey
Level 2
**


View Profile WWW
« Reply #19 on: June 15, 2009, 03:12:16 PM »

Quote
but it doesn't make sense for rendering or input to be globally accessible.
It depends on how you decide to structure your rendering code. For my engines, I tend to use a registry style method of rendering, in that everything registers with the renderer first and then the renderer renders the registry list. The reason for this is so you can do extra batching and culling.
Logged

I like turtles.
Pages: [1] 2
Print
Jump to:  

Theme orange-lt created by panic