wiki:SingletonPattern

TOC(SingletonPattern)?

Singleton Pattern and static class methods - Pros and Cons

In our architecture and in PlatformPT, we use the singleton pattern a lot. Here we can discuss if this is good or bad, or where we can safely use singletons.

This page talks about:

  • The singleton pattern
  • Static methods and member variables
  • Use of explicit classnames in general
  • The registry pattern
  • passing around of function parameters.

Eventually we could also talk about global variables and functions outside of classes.

What is a singleton

Many of the modules and the lib classes are implemented as singletons. This means, these classes have a private constructor, and you need to call PWhateverClass::get(), to get the only one instance of the class. The first time you call get(), a new instance of the singleton will be created, stored in the hidden member variable PWhateverCLass::$_instance and returned. When you call get() again, it will return the stored variable, so the same object.

Sometimes the ::get() function is private itself, and instead the class provides static methods for all the functionality.

Singletons have some advantages compared to global variables:

  • Helper methods and variables can be defined as private, which prevents use of these functions throughout the program.
  • We can use getter and setter methods, to protect inner data from unlimited access.

Singletons receive a lot of criticism because they are not so different from global variables. This increases the dependency of components with each other, and makes unit testing more difficult.

PT/Rox Classes implemented as Singleton:

  • PClasses
  • MOD_right
  • other?

Typical Implementation

A typical singleton implementation could look like this:

class MySingleton
{
    private static $_instance = 0;
    
    private function __construct() {...}
    
    public static function getInstance()
    {
        if(!self::$_instance) self::$_instance = new MySingleton();
        return self::$_instance;
    }
}

More general

Global symbols and variables

Global states

Global symbols

Classes with static method interface

Not all of them are singletons, but eventually they share some of the problems (right?)

Alternatives

Registry Pattern

The PVars class implements the registry pattern, as a singleton. Another possibility would be to pass the registry object around as a function parameter.

A lot more can be written here...

Registry implemented as a singleton

Registry, passed around as a function parameter

Dependency Injection

(we'll have to write a lot more here)

This article is quite interesting,  http://martinfowler.com/articles/injection.html

Discussion

Static methods and singletons:

  • + a text search reveals where a class is used
  • + you always know the type of the object
  • + less indirection
  • - you can't easily change the type of an object your class depends on.
  • - the singleton stores a global state, so it has problems similar to global variables.
    • it obviously has the advantages of global variables too, something you haven't listed here. As for the problems: dependency and coupling is obviously a problem in some cases. However, no matter how you design your software you will have coupling, and having tight couplings to the core parts of your software is not a big problem. The problem comes when all classes are created as singletons - which is a silly thing to do. Fake51
      • yep, it has. I make a new little section to sum up the typical benefits and problems with global variables. lemonhead

Global variables and symbols in general

  • - If you manipulate the value of a global variable, this may have side effects on all the rest of the app. Which means, theoretically every line of code can get in conflict with every other line of code in the application.
  • - The dependency of a component on external global symbols is not apparent in the interface of that component. A singleton can be used in any place in the class implementation, so you would have to scan the entire code of the component to find out where it depends on external services.

Explicit classnames in general

  • Whenever we use explicit class names in many places, it will be hard to replace the resulting object with something else. This does not only apply to singletons and static methods, but also to constructors.
    • I'm not entirely sure what you're referring to here. Why did you put this under singleton pattern? I don't think it is a positive or negative point about anything but our programming practices. Fake51
      • I agree, the title of this article is too narrow for the scope of this discussion. Maybe we can rearrange things when we are finished. lemonhead

Passed around parameters:

  • + you are flexible to change the type of object
  • + you can do UnitTesting?. (will we ever do that?)
  • + you can reuse your code in environments where the class is not defined, or where the classname has another meaning.
  • - you can't be sure about the type of object.
  • - you can't do text search for where a class is used.

Dependency injection:

  • + flexible reuse of classes
  • + may help us with reusing unmodified 3rd party code
  • - reduced transparency: Again, you don't know which class an object has, and you have to look up in several places until you understand what is happening.

Examples in BW-Rox

class MOD_right (Singleton)

The singleton method MOD_right::get() is used in many places in our applications, even in templates. This means: All these components would fail to work in an environment where MOD_right is not defined, or has another meaning.

A typical use of MOD_rights could be:

if (MOD_rights::get()->hasRight('Words') {
    ... // do something
}

If an object implementing the functionality of MOD_right would be passed around as a parameter instead, and maybe stored with each component, then these components could be used in an environment where MOD_right is not defined. We would simply have to pass the component an adapter/wrapper object, that behaves like an instance of MOD_right.

Or, when doing a  Unit test for the component, we would give it a mock object that can represent all possible behaviours of the $right object. For instance, to see how the component works with full admin rights, we would no longer have to log in as admin, but can give it a mock object that behaves as if the user had admin rights.

In any case, we would still need a place where the instance of MOD_right is created. Here we

class PVars (Registry with static access methods)

Singleton documentation

Singleton discussion

Dependency injection