Use the Singleton pattern sparingly; it tends to result in problems down the road.
Singletons make it so there’s only one instance of a class that manages some state – this can be useful for operations that take a long time and must be synchronized, like a file system API.
class FileSystem {
public:
static FileSystem& instance() {
static FileSystem *instance = new FileSystem();
return *instance_;
}
(const FileSystem&) = delete; // remove the copy constructor
FileSystemprivate:
() {}
FileSystem};
Here’s the base class:
class FileSystem {
public:
virtual ~FileSystem() {}
virtual char* readFile(char* path) = 0;
virtual void writeFile(char* path, char* contents) = 0;
};
Here are some implementations:
class PS3FileSystem : public FileSystem
{
public:
virtual char* readFile(char* path) {
// Use Sony file IO API...
}
virtual void writeFile(char* path, char* contents) {
// Use sony file IO API...
}
};
class WiiFileSystem : public FileSystem
{
public:
virtual char* readFile(char* path) {
// Use Nintendo file IO API...
}
virtual void writeFile(char* path, char* contents) {
// Use Nintendo file IO API...
}
};
Then turn it into a singleton:
class FileSystem {
public:
static FileSystem& instance();
virtual ~FileSystem() {}
virtual char* readFile(char* path) = 0;
virtual void writeFile(char* path, char* contents) = 0;
protected:
() {}
FileSystem};
And we can initialize it like so:
& FileSystem::instance()
FileSystem{
#if PLATFORM == PLAYSTATION3
static FileSystem *instance = new PS3FileSystem();
#elif PLATFORM == WII
static FileSystem *instance = new WiiFileSystem();
#endif
return *instance;
}
This doesn’t solve the fact that these are just glorified global vars:
Globals:
Make it hard to reason about code. If a function is affected by or touches a global variable, your reasoning becomes non-local, you have to look at the function and at global state.
They encourage coupling. Global variables are easy to entangle with other code even if you don’t mean it – therefore they get bound into other code when you didn’t mean to, turning your architecture into a ball of mud.
They aren’t concurrency-friendly: Games are multi-threaded now for maximum performance, and having globally readable and writable variables is one way to create race conditions.
Convenient access is why we turn to the singleton, especially for cases like logging. But when we do this, we can no longer create more than one logger. What happens when we need to write to more than one log file? We can’t anymore. If we want to provide a filepath, every line of code that uses it and the class itself must break.
If you want to initialize everything at start time, you don’t want lazy instantiation. To do so, you might write this:
class FileSystem {
public:
static FileSystem& instance() { return instance_; }
private:
() {}
FileSystem
static FileSystem instance_;
};
Do we really need the class?
A lot of singleton classes are “managers”. Classes that babysit another class.
Instead of doing this, allow the object to manage its own state.
class Bulllet {
public:
int getX() const { return x_; }
int getY() const { return y_; }
void setX(int x) { x_ = x; }
void setY(int y) { y_ = y; }
private:
int x_, y_;
};
class BulletManager {
public:
* create(int x, int y) {
Bullet* bullet = new Bullet();
Bullet->setX(x);
bullet->setY(y);
bullet
return bullet;
}
bool isOnScreen(Bullet& bullet) {
return bullet.getX() >= 0 &&
.getX() < SCREEN_WIDTH &&
bullet.getY() >= 0 &&
bullet.getY() < SCREEN_HEIGHT;
bullet}
void move(Bullet& bullet) {
.setX(bullet.getX() + 5);
bullet}
};
Have the class manage its own state and initialization:
class Bullet {
public:
(int x, int y) : x_(x), y_(y) {}
Bullet
bool isOnScreen() {
return x_ >= 0 && x_ < SCREEN_WIDTH &&
y_ >= 0 && y_ < SCREEN_HEIGHT;
}
void move(int x, int y) { x_ += x; y_ += y; }
private:
int x_, y_;
};
We can try and find a way to support single instance instantiation:
class FileSystem {
public:
() {
FileSystemassert(!instantiated_);
instantiated_ = true;
}
~FileSystem() { instantiated_ = false; }
private:
static bool instantiated_;
};
bool FileSystem::instantiated_ = false;
Just pass it in. This adds more arguments to our functions, but gives us the most control.
Get it from the base class. Most game architectures have shallow but wide inheritance hierarchies, so have the base GameObject class control the log and have objects derive from it.
class GameObject {
protected:
& getLog() { return log_; }
Log
private:
static Log& log_;
};
class Enemy : public GameObject {
void doSomething() {
().write(;
getLog}
};
Assume there’s already a singleton Game object:
class Game {
public:
static Game& instance() { return instance_; }
// Functions to set log_, et. al. ...
& getLog() { return *log_; }
Log& getFileSystem() { return *fileSystem_; }
FileSystem& getAudioPlayer() { return *audioPlayer_; }
AudioPlayer
private:
static Game instance_;
*log_;
Log *fileSystem_;
FileSystem *audioPlayer_;
AudioPlayer };
You can get your audio through the game instance:
::instance().getAudioPlayer().play(VERY_LOUD_BANG); Game