How to get rid of all these globals?

Started by
12 comments, last by Julian90 16 years, 11 months ago
My game code is filled with globals. For example, I have a "Renderer" namespace, which contains a whole bunch of globals that anyone can access. And a "SoundManager" namespace, "Input", etc. etc. ad nauseum. I don't think you could call them singletons, since they're not really *objects* that are instantiated only once. They're merely groupings of globals. As I start adding more and more, the system seems to be getting messier and messier. Functions from anywhere, at any time, can access and modify the internal state of the renderer - something that only the renderer itself should be able to do. And I can think of some other problems which could arise from my current setup. What I want to know is: what would be an elegant solution to my problem?
NextWar: The Quest for Earth available now for Windows Phone 7.
Advertisement
A simple solution would be encapsulation.

You need to encapsulate certain logic in their (logical) classes. For example, all the rendering code should be encapsulated in the Renderer class. All data are to be private members of that class, which only the class itself can access.
Quote:Original post by Sc4Freak
My game code is filled with globals. [...] a whole bunch of globals that anyone can access.

Change the way of accessing that information:

Put the actual data in the class that manages it and let the rest of your app get the data only through the instance of that data manager class.

namespace FooThings{   class Foo   {      #region Variables      //Repeat this for each former global      private int formerGlobal;      public int FormerGlobal      {         get { return formerGlobal; }         set { formerGlobal = value; }      }      //---      #endregion      #region Constructor      public Foo(int formerGlobal)      {         this.formerGlobal = formerGlobal;      }      #endregion   }}namespace MainThings{   class FooUser   {      #region Variables      private Foo fooBar;      #endregion      #region Constructor      public FooUser()      {         this.fooBar = new Foo(42);      }      #endregion      #region ACCESS TO NON GLOBAL      private void Method()      {         int a = this.fooBar.FormerGlobal;      }      #endregion   }}
Zanshibumi: Why are you doing this:
private int formerGlobal;public int FormerGlobal{     get { return formerGlobal; }     set { formerGlobal = value; }}

When you can just do:
public int FormerGlobal;

You don't gain anything from making FormerGlobal a property as you can always change it later without having to change any code which uses it. It doesn't increase encapsulation and it leads to duplicated information which is bad.

Better yet though why not do:
// I personally don't like the idea of a central "Renderer" class but// its the simplest to change to from your current setupstruct Renderer{    Renderer( /* ... */ ) { }    void SemanticallyMeaningfulBehavior( /* some params */ )    {        // do something possibly involving formerGlobal    }private:    FormerGlobal formerGlobal;};// now use it...struct Foo{    void SomethingRenderingRelated(Renderer& renderer)    {        renderer.SemanticallyMeaningfulBehavior( /* some param *);    }};
Quote:Original post by Julian90
You don't gain anything from making FormerGlobal a property as you can always change it later without having to change any code which uses it. It doesn't increase encapsulation and it leads to duplicated information which is bad.

It's a common convention not to use public class-level variables.

google "coding style" or "bad practices" and you should find info about why not to do it.


P.S.: I do as you say some times, but when trying to explain something, I try to use standard practices as much as I can.
Quote:
Zanshibumi: Why are you doing this: ....

I must agree with Julian here. Trivial properties (that just return or set the underlying field) in C# are a waste. Unlike C++, changing a field from public to private-but-exposed-via-a-property is trivial even without the powerful refactoring capabilities offered by most IDEs for C#, because in C# the change does not require a change at sites that use the public field, whereas in C++ it would (you'd have to add parentheses for the accessor call and potentially reshuffle the line). The arguments for trivial accessors in C++ are already pretty frail, but they fall down completely in C#.

Quote:
It's a common convention not to use public class-level variables.
google "coding style" or "bad practices" and you should find info about why not to do it.

It's a common C++ convention. I'm not arguing that encapsulation is bad, but a trivial property, a public pair of trivial set/get methods, and a public field are all equally "encapsulated" (that is to say, not at all). The rest is just implementation detail. The implementation detail makes for more work in C# and adds no benefit, whereas in might in C++ (using get/set methods does allow you to put constraints in later without changing client code, but that does not apply to C#).

Trivial properties are not good C# convention.





Of course, all of this is moot. The original poster isn't using C# (because he has global variables in namespaces). The notes about encapsulating all his globals does, of course, still apply.
I read about that a lot of time ago and decided to follow the standard in this case.

For whoever reads this, Here you have a much more detailed discussion about the same.

I decide what are good conventions or bad conventions for me when I make my own code. If it's for someone else, I try to stick to the standard. At work I just use the one imposed.
Quote:Original post by Sc4Freak
For example, I have a "Renderer" namespace, which contains a whole bunch of globals that anyone can access.


Could just make those globals static (file scope)...

Same with internal functions.
Anthony Umfer
Quote:Original post by Zanshibumi
Change the way of accessing that information:

Put the actual data in the class that manages it and let the rest of your app get the data only through the instance of that data manager class.
You don't really improve encapsulation that way though. Consider:

Case 1: We have two global variables, x and y. These variables can be altered from anywhere in the program.
x = 1;y = 1;


Case 2: We take our two global variables and make them public properties of an object. If the object has global scope the variables may still be altered from anywhere in the program, but the line of code to do so will be slightly longer.
someobject.x = 1;someobject.y = 1;


Case 3 (your suggestion): We make the two properties private and add public accessor(get)/mutator(set) methods. If the object has global scope the variables may still be altered from anywhere in the program, but the line of code to do so will be slightly longer.
someObject.setX(1);someObject.setY(1);


So, where's the benefit of Case 3 as compared to Case 2 in terms of encapsulation?


Quote:As I start adding more and more, the system seems to be getting messier and messier. Functions from anywhere, at any time, can access and modify the internal state of the renderer - something that only the renderer itself should be able to do.
Why did you design it like that?

Encapsulate the data correctly and then pass the information only to where it's actually required. We'd need to see specific examples to make more concrete suggestions.

- Jason Astle-Adams

Quote:Original post by Kazgoroth
someobject.x = 1;someobject.y = 1;


Case 3
someObject.setX(1);someObject.setY(1);


So, where's the benefit of Case 3 as compared to Case 2 in terms of encapsulation?

Case 3 would be
someObject.X = 1;
too. (in C#)

Which means no benefit or loss, from outside the class.

However, the class internal storage of that information could change in the future without changing in any way the access from outside nor having to recompile.

In case3, if you release a dll and some people start using it in environments you can't interfere with. You can still change the way that information is stored inside the class (for example, X and Y in a Point structure, a file or a remote server) without modifying the external access to that information.

You could argue that the same could be done simply by having:
public int x;
and later changing it to:
public int x{   get {return GetVariableFromFile("x");}   set {SaveVariableintoFile("x", value);}}
However it could bring problems


Anyway, I just set it like that because that's how I've seen teachers usually put code; every standard followed (to the extent of my knowledge and lack of custom).
As you all seem to think it's simply wrong to use that way of coding in explanations, I'll stop using it.

This topic is closed to new replies.

Advertisement