RSS Feed

Peter Goodman's blog about PHP, Parsing Theory, C++, Functional Programming, Applications,

Globals, Singletons and (Public) Static Members

I've decided that a solid explanation of where global variables, singletons and public static members of a class should be used is in order. Even though none of these three things are the same, they all share a single commonality: they can be accessed anywhere and in any scope. Lets get to it then...

Global Variables

So what are global variables in PHP? Global variables include the super-globals: $_REQUEST, $_SESSION, $_COOKIE, etc. as well as user-defined global variables through the $GLOBALS array. They are variables that can be access in any scope by use of the 'global' keyword (for user-defined globals) or simply by using a superglobal.

The idea of having variables that can be accessed in any scope is an interesting one, and also a scary one. Although global variables can be mind-bogglingly convenient for simple problems such as passing a database layer around or accessing user information from random functions, it's also very bad practice. But why? Well, global variables allow functions to have information that you simply don't know about. When you call a function and pass argument to that function, you know what information that function has access to because you have specifically given it that access. When you start to use global variables, it becomes increasingly difficult to track what functions have access to what information.

Here's a farfetched example: assume you and I are best friends and we are both interested in the same girl/guy (whatever your preference is) and s/he is named Devon (it goes both ways). You really like Devon and every day you tell me that you like him/her. What you don't know is that I have supernatural powers and I can read Devon's mind (not yours though). So one day you and Devon go out for lunch and you come back and tell me all about it. You know I like Devon so you spare me some of the *better* details, but I already know everything because I've read Devon's mind. If you knew that I know what you knew (and more!) then you would be very concerned. But, you don't know that I have all of this inside information. Of course, I'm your best friend so I would never do anything to hurt you so we would go along living like this.

Now, I might have got a bit carried away there, but there is a very important thing to get out of this. As time progresses, our relationship (you and I) would become more and more damaged. Why? Well, I would know that every night when you would get back from your "stroll" that you are actually lying to me.

Obviously your code will not lie to you, but as time progresses and your projects get bigger and more complex, it will become increasingly difficult to maintain your project because:

  • Information will be harder to track
  • You will become more dependent on global variables
  • Removing globals would require a complete refactor / recode.
  • Devon gets a sex change.
Okay, maybe not that last one, but I just had to put it in.

Singleton Pattern

If you don't know what the singleton pattern is, here's a bit of code:

// class:
class Singleton
{
    private static $instance = NULL;

    private function __construct()
    {
        // hazaaa
    }

    public static function getInstance()
    {
        if(!self::$instance instanceof self)
        {
            self::$instance = new self;
        }

        return self::$instance;
    }

    public function readDevonsMind()
    {
        // supernatural powers here.
    }
}

// usage:
$singleton = Singleton::getInstance()->readDevonsMind();

Singletons don't actually need to be in classes, but for the sake of this article, it is in one. The function of a singleton is to return the same instance of a class no matter where it is called/used. This property of the singleton pattern makes it especially useful for holding data such as and array of information what would be otherwise global, a registry, an abstraction layer, etc.

Now, you're probably thinking that I've just described a global variable masquerading as a class. You're half right. Singletons can be accessed regardless of what scope you are in, so the same information problems as with global variables described above are implied.

'getInstance' is a static function which has global scope within the 'Singleton' namespace. Really, this just sounds like another global variable but with the added advantage of using a function instead of a variable. So yes, there IS and advantage to singletons. That doesn't make them any better in terms of coding practice, but it does make them stronger. The fact that a function is used to return the same thing every time it is called means that we can do cool things within that function. Basically, we are saying that singletons by definition are more abstract than global variables, and that's why they are sometimes viewed as a better solution to some problems.

So singletons are sounding kind of good sofar. Well, don't get too comfortable with singletons. The reason why I say this is that I don't want you to get lured in by the convenience of singletons. It's not that bad to use them in your code, but you shouldn't use too many. As mentioned above they are useful for such things as registries, database layers, etc.

For example, if you are using a database abstraction layer but are not working in an OO environment, then a singleton will generally fit the bill. Why? Well you don't want to create a new connection to a database every time you need one, so passing the same connection around makes sense. But, lets say you are working in an OO environment. Sometimes singletons give classes too much power. That sounds a bit ridiculous at first, but singletons allow classes to make connections to other classes that they necessarily shouldn't know how to make. This is a very odd idea. How can one class that should end up linking to another class have the power to link to that class and it's a bad thing? Well, it's because of a thing called 'coupling'.

"In object-oriented programming, coupling is a measure of how strongly one class is connected to another." [1] The whole idea in OO programming is to abstract things as much as possible, e.g.: when dealing with databases, we will abstract it to database abstraction layers using separate layers for each type of database (mysql, mssql, postgresql, etc). You might say that in OO programming, you try to separate things/tasks ans simplify them to only their component parts (e.g. one class for sessions, one for request variables, etc.).

Enough explaining, what affect do singletons have on coupling? Well, in your average OO program, you have very low coupling, meaning that classes are all well separated and don't mix stuff up. If this idea is still fuzzy, think about writing an essay and that each class in your program is a paragraph in your essay. Generally speaking, a paragraph represents one idea. So to have low coupling (good), you want to keep each paragraph with only one idea. If you are using singletons, you cause your classes to have high coupling, (mixing your ideas up in your paragraphs).

Just like I mentioned earlier, the reason why singletons cause high coupling is because they give too much power to your classes. They give your classes the power to make connections to other classes that they shouldn't necessarily be able to do. You might be thinking: "so what?", well, if you've designed a good program, everything should come together nicely in the end and you shouldn't need to rely on singletons. It's a lot like table manners... If you have good table manners, you won't need to lean on your elbows/foreams. And well, if you have bad table manners, at least chew with your mouth closed and try to use as few singletons as possible.

Public Static Members (PSM)

Woah, was that a long section or what!? You must be so bored. I'll make this part quick. So, what do I have to say about public static members/methods of a class? Well, they are just functions/variables that have global scope but are preceded by a namespace. Now, I won't tell you not to use them, but I will give you an insight into them...

// class:
class Math
{
    public static function sin($x)
    {
        return sin($x);
    }
}

// usage:
echo Math::sin(30);

Now this looks very familiar to a singleton. The main difference between a PSM of a class and a singleton is that a PSM doesn't have a state while a singleton does. So what is 'state'? Simply put, if something is stateful, it keeps track of data, records it, can process it, etc. So how is a static method not stateful? Well, it all has to do with the nature of the function itself.

By being a PSM (and not being a singleton), a function has no way of remembering anything. It's impossible to store data in it simply because even if you passed a lot of stuff into it, the next time you called it, it would return nothing (assuming the return value was meant to be data or something). In the above Math::sin function, all that function can really do is parse/calculate/figure something out. There really is no other use for it. By being static, that function can't use '$this', so it's just a normal function with 'Math' and two fany colons before its name.

This is a very interesting distinction one needs to make between a static member of a class (stateless), and a static member of a class that is a singleton (stateful). Why is this important you ask? (I hope) Well, with every program, things are subject to change. Let's say we've got a program that uses a new and experimental forumla to calculate something interesting. We use this forumal all over our program, and other formulas like it, so we've grouped them all together into one class. We have two options.

Now, this section is supposed to be about PSMs, but it also has to be able singletons again because being able to recognize when to use each one is very important. Back to our scenario.. Devon likes me better. Whoops! Sorry, let that slip. So we are dealing with lots of complex and experimental functions. Anyone whose ever programmed anything knows that over time slight modifications are always needed to keep the program in ship shape. That means that we are going to need some flexibility in our functions. Assume it's been one year since we've implemented our cool math junk and it turns out that the function is completely wrong and we need a new one. Well, we should rename it and replace it. That means changing the names in the countless areas that it's used in the program and then re-uploading everything, etc, etc. For such a small change of a function, it cascades to become a big problem. Now think if we used a singleton there instead. All we would need to do is change the class in the factory 'createInstance' method. This allows A LOT of flexibility. In fact, using a singleton in this case allows us to use parent/child classes, etc.

So... you want me to use singletons now? Well, what I want you to when programming with PSMs is to try and forsee future maintenance. Under these circumstances, the singleton pattern allows you to abstract business logic out of the function that's being used all around your program, and that's a good thing. However, if you need to do a simple thing, such as the Math::sin function above, a static member function will serve you well.

Conclusion

Don't use global variables. They are evil. Stay away from them! If you need that type of functionality, abstract it to the more flexible but on-the-fence-of-evil-and-good singleton. Be aware that the use of singletons will make it harder to debug your applications because of high coupling, and don't forget about Devon and I and giving functions access to too much information. Finally, when you are coding and are using public static members, think about future maintenance of the code and ask the question: will I need the flexibility of a singleton or is this never going to need changing.

Sources / Inspirations


Comments


Comment