Reputation: 185
I've got this quarrel with a friend: he says that I should use a singleton, but I read everywhere that singletons are rarely a good idea.
My use case is that I'm implementing this game engine (too little experience with that, so I'm trying to learn one way or the other) where I've got a class Application
, for instance:
class Application{
private:
SomeClass1 someClass1;
SomeClass2 someClass2;
...
}
class SomeClassN{
private:
Application& app;
SomeClass14 someClass14 // meaning that Application& will be needed deeply into the tree of "components"
...
}
It should be added that not all classes will be of type SomeClassX (not all classes will need to contain Application&).
My friend tells me to make Application a singleton, while I just prefer passing it and storing it. Actually I would create a base class:
class ApplicationContainee {// temporary name, please kindly suggest a correct name
protected:
Application& app;
}
class SomeClassX : ApplicationContainee {
}
I'm afraid that even this approach might be disastrous or maybe equivalent of having a singleton.
Summing up:
ApplicationContainee
?Upvotes: 0
Views: 1352
Reputation: 29028
Singleton pattern is indeed a pattern to avoid. Apart from introducing serious problems when it comes to writing unit tests, managed languages that uses garbage collection like Java and C# will suffer from potential memory leaks du to the way the garbage collector treats static references. In other words, static instances will reside in the memory for the lifetime of the application. In case the static instance also uses unmanaged resources, you block this resources and risk to keep the allocated resources alive after the application has closed.
It really depends on the actual scenario, but we can say that when applying or implementing proper programming principles we can avoid the Singleton pattern and its disadvantages.
The key is to avoid static references in general. If you have the following code that calls a static method too read from a database:
public User GetUser(int id)
{
User user = StaticDatabaseHelper.GetUser(id);
// Do something
return user;
}
You can only test this method by making real calls to the actual database. You can't fake it. You see thta if you are not writing unit tests, this disadvantage could be irrelevant. But what is always relevant is the bad design that is very likely introduced by making instances and methods globally available. The fact that the author decides to make a state (static properties or instances (Singleton) or methods (static classes) globally available is the result of missing application design. It's something beginners do as they don't know more advanced pattern and principles or architecture in general. Beginners don't care about lifecycle management or encapsulation and data hiding. Global references will scatter through out your code rapidly, making it a nightmare to maintain. The only static members should be constants.
The best way to avoid the Singleton pattern is to introduce aggregation (or dependency injection). You would typically inject/pass a shared instance to a constructor instead of exposing a static instance. This usually requires a common composition root for all the dependent instances:
class A
{
// Constructor injection
public A(Application sharedApplicationInstance)
{
this.application = sharedApplicationInstance;
}
private Application application;
}
class B
{
// Constructor injection
public A(Application sharedApplicationInstance)
{
this.application = sharedApplicationInstance;
}
private Application application;
}
public Main()
{
Application sharedApplicationInstance = new Application();
// Composition root
A a = new A(sharedApplicationInstance);
B b = new B(sharedApplicationInstance);
}
Upvotes: 1