Chapter 3

When an application needs one, and only one,
instance of a particular object, proper enforcement of
that object’s uniqueness and appropriate global
access to the object can be problematic.
This is particularly useful when one object is needed
to coordinate actions across a software system.
One problem associated with
this pattern is the fact that it
complicates unit testing, since
it introduces a global state
into the software system.
Chapter 3 – Page 45
Creational Pattern: Singleton
The class of the single
instance is responsible for
access and "initialization
on first use“.
The single instance is a protected static attribute in order to
guarantee that a new instance is created if one doesn’t
already exist, and if one does exist, a reference to that
instance is accessible.
The constructor is private in order to ensure that the object
can only be instantiated via the constructor.
Chapter 3 – Page 46
The Singleton Pattern
#include
#include
#include
#include
<string>
<iostream>
<fstream>
<vector>
using namespace std;
class Logger
{
public:
static const string kLogLevelDebug;
static const string kLogLevelInfo;
static const string kLogLevelError;
// Returns a reference to the singleton Logger object
static Logger& instance();
// Logs a single message at the given log level
void log(const string &inMessage, const string &inLogLevel);
// Logs a vector of messages at the given log level
void log(const vector<string> &inMessages, const string& inLogLevel);
protected:
// Static variable for the one-and-only instance
static Logger sInstance;
// Constant for the file name
static const char* const kLogFileName;
// Data member for the output stream
ofstream mOutputStream;
private:
Logger();
~Logger();
};
Chapter 3 – Page 47
C++ Code for a Logger Singleton
const char* const Logger::kLogFileName = "log.txt";
// The static instance will be constructed when
// the program starts and destroyed when it ends.
Logger Logger::sInstance;
Logger& Logger::instance()
{
return sInstance;
}
Logger::~Logger()
{
mOutputStream.close();
}
Logger::Logger()
{
mOutputStream.open(kLogFileName);
if (!mOutputStream.good())
cerr << "Unable to initialize the Logger!" << endl;
}
void Logger::log(const string &inMessage, const string &inLogLevel)
{
mOutputStream << inLogLevel << ": " << inMessage << endl;
}
void Logger::log(const vector<string> &inMessages, const string &inLogLevel)
{
for (int i = 0; i < (int)inMessages.size(); i++)
log(inMessages[i], inLogLevel);
}
Chapter 3 – Page 48
const string Logger::kLogLevelDebug = "DEBUG";
const string Logger::kLogLevelInfo = "INFO";
const string Logger::kLogLevelError = "ERROR";
vector<string> items;
items.push_back("item1");
items.push_back("item2");
Logger::instance().log(items, Logger::kLogLevelError);
}
Final
contents
of log.txt
DEBUG: test message
ERROR: item1
ERROR: item2
Chapter 3 – Page 49
void main()
{
Logger::instance().log("test message", Logger::kLogLevelDebug);
• The Singleton pattern ensures that the class (not the
programmer) is responsible for the number of instances
created.
• Unlike static variables, the Singleton pattern is extensible, so
if the software design later changes so a larger (but still
controlled) number of instances is needed, only the instance()
operator needs to be changed.
• Unlike global variables, singletons don’t pollute the
namespace with unnecessary variables.
Chapter 3 – Page 50
Singleton Design Advantages