Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Monostate Pattern

Last updated Jan 1, 2003.

Design patterns were the hottest commodity of the 1990s, promising a cure to almost every ailment of the software industry. The tremendous hype led programmers to believe that a catalog of a few dozen recipes was all they needed to accomplish every programming task painlessly and swiftly. As usual, unrealistic expectations led to a bitter disappointment afterwards.

Today, as the pattern enthusiasm has abated, a more down-to-earth analysis is possible. Patterns will not solve all the problems of software developers. Nor will do they replace resourcefulness, experience, skill and intelligence. Yet, they offer a few shortcuts that simplify certain recurring programming tasks, very much like standard algorithms.

Today, I will explore the monostate pattern and its applications.

Problem Analysis

Sharing a single resource among multiple users or processes is a common scenario. Several applications on the same computer need to share a single modem; a single exchange rate table is shared by different departments of the same department store; a weather report is shared by different workstations of flight controllers at the same airport, and so on. How can you ensure that all users access the shared data safely? More importantly, how can you ensure that changes propagate immediately and automatically to all users?

Let's look at a concrete example. Suppose you have a weather report table containing the maximum and minimum temperatures, wind velocity, visibility conditions etc., of a certain region. These data are constantly updated and need to be shared by all flight controllers of a certain airport.

A naïve programmer might be tempted to use a file to store the data. Although this solution could work, it suffers from several shortcomings: accessing a physical file by multiple users could cause deadlocks and bottlenecks; the application in question must know where the file is located and how it's formatted. Worse yet, granting dozens of users direct access to a file could incur security problems and network congestion. Finally, how are end users informed of changes made to the file?

A better solution is to encapsulate the weather report in a class. You want to ensure that every user and application module gets a private object instance of that class, while ensuring that these objects always share the same state. Furthermore, any change in the state of the class should propagate to other objects automatically and immediately. This is exactly what the monostate pattern provides.

Enter Monostate

A monostate class contains only private static data members and public non-static member functions to access these members. This way, all objects of this class share the same state.

Here's a class that represents the current weather conditions at a certain region:

class Weather
{
private:
 static int max_temp; //all temperatures are in Fahrenheit
 static int min_temp; 
 static int current_temp;  
 static int wind_velocity; //mph
 //...
};

The definitions of the static members outside the class also initialize them:

int Weather::max_temp=53;
int Weather::min_temp=36;
int Weather::current_temp=50;
int Weather::wind_velocity=14;

In the real world, you'd probably use a function that reads the current weather conditions from a remote meteorology server or an external telemetry device that measures temperatures, humidity etc:

int Weather::current_temp=thermometer.get_current_temp(); 

Obviously, class Weather also defines member functions to access these values:

class Weather
{
//
public:
 int get_max_temp() const { return max_temp;}
 int get_min_temp() const { return min_temp;}
 int get_current_temp() const { return current_temp;}
 int get_wind_velocity() const { return wind_velocity;}
// 
};

Putting It To Work

Each controller's desktop runs a module of the same flight control application which uses its own Weather object. This means that there are several live Weather objects at any given time:

Weather weather1, weather2;

However, they all share the same state, thereby ensuring that every controller gets the exactly the same weather report:

//first controller's desktop
int current1=weather1.get_current_temp();
//second controller's desktop
int current2=weather2.get_current_temp();

It doesn't matter which object is queried. The same member function will return an identical result even if it's called from different objects. This solves the problem of data sharing neatly.

That said, I haven't shown the real strength of the monostate pattern yet. What happens when the weather conditions change and Weather's data members need to be updated accordingly? To change the weather conditions, add another class called Admin, which is a friend of Weather. The new weather conditions propagate immediately to all instances of Weather like this:

class Admin;
class Weather
{
friend class Admin;
//
};
class Admin
{
public:
 Admin(const string &password) {} //authorize
 void set_min_temp(int min) {Weather::min_temp = min;}
 void set_max_temp(int max) {Weather::max_temp = max;}
 void set_current_temp(int temp) {Weather::current_temp = temp;}
//
};

Suppose the current temperature is now 42. The system administrator (or a daemon process) updates it like this:

Admin admin("mypassword");
admin.set_current_temp(thermometer.get_current_temp());

After calling set_current_temp(), every Weather object knows that the current temperature is 42 degrees. If any of the flight controllers performs the following query,

//controller 1
current1=weather1.get_current_temp(); //42
//controller 2
current2=weather2.get_current_temp(); //42

All existing Weather objects will report the new current_temp value. Thus, we have accomplished the second design requirement -- ensuring immediate and automatic propagation of state changes to every object.

Summary

In terms of its goals, monostate is similar to the singleton pattern, though much simpler to implement and use. Using static data members exclusively ensures that the same state is shared by all objects of the same class, regardless of their creation time. By changing a static member's value, you update the state shared by all objects immediately and automatically. Notice however that the implementation shown here isn't thread-safe; slight modifications (that are beyond the scope of this article) will make it thread-safe, if necessary.

Books

Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides has been the ultimate pattern catalog since it was published in 1994. Although many patterns were developed after its publication (monostate, for instance), it's still one of the most influential programming books ever written.

Safari Books

Real-Time Design Patterns: Robust Scalable Architecture for Real-Time Systems by Bruce Powel Douglass focuses on a more specialized application domain, namely real-time and embedded systems. Using state-of-the-art features, such as advanced template programming and UML diagrams, it addresses common (and not so-common) programming tasks, such as building a message queue, garbage collection, virtual machines, and many other constituents of robust and time-critical systems.