Chapter 4

Building with Design Patterns

IN THIS CHAPTER

check Understanding what design patterns are and how you can use them

check Implementing an observer pattern

check Building a mediator pattern

When you work as a developer, eventually you start to notice that you do certain things repeatedly. For example, when you need to keep track of how many instances of a certain class you create, you define a static property called something like int InstanceCount;, include a line that increments InstanceCount in the constructor, and include a line that decrements InstanceCount in the destructor. You make InstanceCount private and include a static method that retrieves the value, such as getInstanceCount().

Because you use it so often, it becomes a pattern. The first time you used it, you had to think about it — how to design and implement it. Now, you barely have to think about it; you just do it. Thus, it’s a design pattern that you use.

This chapter takes a practical look at design patterns that you use when creating applications. It helps you understand why using design patterns reduces development time, makes code less error prone, and improves application efficiency. The chapter delves just a bit into history and usage, with the usage considerations relying on the singleton pattern as a starting point.

You also see how to create and use two common design patterns: observer and mediator. You may or may not actually use these patterns in your applications, but by seeing how they’re put together, you can find or create other patterns that will make your development process easier.

Delving Into Pattern History

Way back in 1995, a book became an instant bestseller in the computer programming world: Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. The four authors of this groundbreaking book would become known in the field of programming as The Gang of Four.

The Gang of Four drew on a body of knowledge in the field of architecture — not software architecture, but rather the field of those people who build tall buildings, brick-and-mortar style. That kind of architecture has been around for at least two-and-a-half centuries, so the field is more mature than the field of software development. And, in the field of building design, people have come up with common ways to design and build buildings and towns, without having to start over from scratch every time with a new set of designs. Christopher Alexander wrote a book in 1977 called A Pattern Language (see http://www.patternlanguage.com/ca/ca.html for details), which teaches the major concepts of building architecture by using patterns. The Gang of Four drew on this knowledge and applied it to software development principles.

In their book, The Gang of Four point out something that seems obvious in hindsight (but then again, great discoveries are often deceptively simple): The best software developers reuse techniques in the sense of patterns. The description of the class that keeps an instance count is an example of a technique that can be used over and over.

Now, if you heavily explore the field of object-oriented programming (and computer science in general, really), you often see the term reusable. One of the goals of object-oriented programming is to make code reusable by putting it in classes. You then derive your own classes from these classes, thereby reusing the code in the base classes.

You could probably put an instance-counting class in a base class and always derive from it. But for some other designs, using a base class doesn’t always work. Instead, software developers apply the same design to a new set of classes. The design is reused, but not the code. That’s the idea behind design patterns. You don’t just write up your design patterns and stuff them into a bunch of base classes. Instead, you know the patterns. Or you keep a list or catalog of them. You can find a list of the seven most important design patterns at https://medium.com/educative/the-7-most-important-software-design-patterns-d60e546afb0e.

Introducing a Simple Pattern: the Singleton

In this section, you discover how to create a design pattern so that you can see what it is and, more important, how you can use it. You look at a situation in which you need a class that allows only one instance to exist at any given time. You’ve come across this need many times. For example, you may have a class that represents the computer itself. You want only one instance of it. You also may have a class that represents the planet Earth. Again, you need only one instance. If people try to create a second instance of the class in their code, they will receive a compiler error. The following sections discuss how to perform this task in C++ using the singleton pattern (which ensures that only one instance exists at a time).

Using an existing pattern

You could spend a couple hours coming up with an approach to the problem of creating only a single instance. Or you could look at a pattern that already exists somewhere, such as what this section shows you.

To understand how to create the singleton pattern, you need to first understand an unusual concept that many C++ programmers don’t usually consider: You can make a constructor of a class private or protected, which prevents someone from directly creating an instance of a class. To make this process work, you include a static method that creates the instance for you. Static methods don’t have an instance associated with them. You call them directly by giving the class name, two colons, and the method name. Fortunately, the static method is a member of the class, so it can call the constructor and create an instance for you.

Here’s how you make a singleton class: First, make the constructor private. Next, add a public static method that does the following:

  1. Checks to see whether a single instance of the class already exists. If so, it returns the instance’s pointer.
  2. Creates a new instance and returns its pointer if an instance doesn’t already exist.

Finally, where do you store this single instance? You store its pointer in a static property of the class. Because it’s static, only one property is shared throughout the class, rather than a separate variable for each class instance. Also, make the variable private so that users can’t just modify it at will.

And, voilà — you have a singleton class! Here’s how it works: Whenever you need the single instance of the class, you don’t try to create it. (You’ll get a compile error! Yes, the compiler itself won’t let you do it.) Instead, you call the static method.

Warning The singleton pattern won’t prevent different programs on the same machine from creating multiple instances of the class (one for each application). This pattern works only within a single application. Consequently, if you need to ensure that a class isn’t instantiated more than once on a machine as a whole, you need to use some form of globally unique identifier at the operating system level, a topic that’s outside the scope of this book.

Creating a singleton pattern class

It’s time to see the singleton pattern at work. Listing 4-1 from the Singleton example shows how to create such a class:

LISTING 4-1: Creating a Singleton Class

class Planet {
private:
static Planet *inst;
Planet() {}
~Planet() {}
public:
static Planet *GetInstance();
};

Planet *Planet::inst = 0;

Planet *Planet::GetInstance() {
if (inst == 0) {
inst = new Planet();
}
return inst;
}

int main() {
Planet *MyPlanet = Planet::GetInstance();
cout << "MyPlanet address: " << MyPlanet << endl;

Planet *MyPlanet2 = Planet::GetInstance();
cout << "MyPlanet2 address: " << MyPlanet2 << endl;
return 0;
}

To use this class, you can’t create an instance directly. Instead, you call the GetInstance() method:

Planet *MyPlanet = Planet::GetInstance();

Remember You call this any time you want to get a copy of the single instance, which may include creating an instance when one doesn’t exist. Every time you call GetInstance(), you always get a pointer to the same instance. When you run this code, you see output like this, which confirms that MyPlanet and MyPlanet2 both point to the same instance:

MyPlanet address: 0x3faf08
MyPlanet2 address: 0x3faf08

Look at the constructor: It’s private. Therefore, if you attempt something like this somewhere outside the class (such as in main()):

Planet MyPlanet;

you get a compiler error. In Code::Blocks, you get this error:

error: 'Planet::Planet()' is private
error: within this context

Or if you try to create a pointer, you get the same error when you call new:

Planet *MyPlanet = new Planet();

The singleton pattern is about creating and destroying a single instance as needed, so you don’t want anything deleting the instance that the application creates. Just as you would make the constructor private, you would also make the destructor private, as shown in Listing 4-1. If you try to delete an instance after you obtain it, as in

Planet *MyPlanet = Planet::GetInstance();
delete MyPlanet;

then once again you receive an error message — this time, for the destructor:

error: 'Planet::~Planet()' is private
error: within this context

Warning You may be tempted to make a constructor that takes a parameter. You could pass parameters into the GetInstance() method, which would in turn pass them to the constructor. This would work the first time, but there’s a catch: Remember that after the GetInstance() method creates the instance, it never does so again. That means it won’t call the constructor again. Therefore, if you have a class that looks like this:

class Planet
{
private:
static Planet *inst;
Planet(string name)
{
cout << "Welcome to " << name << endl;
}
~Planet() {}
public:
static Planet *GetInstance(string name);
};

and your GetInstance() method has this code in it:

Planet *Planet::GetInstance(string name)
{
if (inst == 0)
{
inst = new Planet(name);
}
return inst;
}

and you make two calls like this:

Planet *MyPlanet = Planet::GetInstance("Earth");
Planet *MyPlanet2 = Planet::GetInstance("Venus");

the results may not be as you expect. You end up with only one instance, which gets created with the first line — the one with "Earth" passed in. In your second call to the GetInstance() method, GetInstance() sees that an instance already exists and does not even use the "Venus" parameter. So be careful if you’re using parameters in constructors.

Watching an Instance with an Observer

A common task in computer programming is when one or more instances of a class (or different classes) need to keep an eye on a certain object and perform various actions when that object changes. In other words, the class that keeps an eye on the others is an observer, hence the name of the pattern. The following sections tell you about the observer pattern and describe its use.

Understanding the observer pattern

You may write an application that monitors various activities around your house when you’re away. Your application could be configurable; you could set it up so that the user can choose various actions to take if something goes awry. You might have the following options:

  • The application saves a note in a file so that you can later review it.
  • The application sends an email (or text for that matter) to you.
  • If the computer is linked to a telephone security system, it can notify the police.
  • The robotic dog can receive a signal to go on high alert.

Each of these actions can exist in a different class, each with its own code for handling the situation. The one about saving a note to a file is easy: You would open a file, write to it, and close the file. The email example might involve obtaining a Simple Mail Transfer Protocol (SMTP) library, using it to create a message object, and then sending the message. To notify the police, your computer would have to be hooked up to an online security system that’s accessible via the phone lines or perhaps via the Internet, and the police would need a similar system at their end. The class for this would send a signal over the lines to the police, much like the way a secret button that notifies the police of a robbery at a gas station works. Finally, you might have a similar contraption hooked up to the brain of your little robotic watchdog, Fido; after receiving a high-voltage jolt, Fido can go on high alert and ward off the intruders. These situations use Observer classes (each class derives from a base class called Observer).

Now, you would also have a class whose object detects the problem in the house. This object might be hooked up to an elaborate security system, and when the change takes place, the computer calls a method inside this object. We call this class the Subject class. So think about what is happening here:

  1. When a security issue happens, the computer calls a method inside the single Subject instance.
  2. The Observer classes have objects that watch the Subject instance. The method in the Subject class then calls methods in each of the Observer objects. These methods take the appropriate action, whether it’s write to a file, notify the police, zap the robotic dog, or whatever.

Here’s the catch: The people using your computer application can determine which Observer classes they want to respond to the event (possibly, via an Options dialog box). The ability of the user to determine which Observer classes to use means that the design must be flexible. In order to obtain this flexibility, you need to add the following requirement: You might add new Observer classes as they come up, so the Subject class must accommodate them all. One Observer might signal a helicopter to fly in and chase a robber who’s making a getaway. But you can’t be sure what you’ll come up with next. All you know is that you may add Observer subclasses and instances of these subclasses. Here are the issues that come up when designing such a set of classes:

  • You could keep a long list of instances inside the Subject class, and whenever an event takes place, the event handler calls a routine in all the Observer instances. The Observer instances then decide whether they want to use the information. The problem with this situation is that you have to call a method within the Observer classes (call into the class), even if the individual instances don’t want the information.
  • You could have each Observer instance constantly check the Subject instance, looking for an event. (This process is called polling.) The problem here is that this process can push the computer to its limits, believe it or not: If every single Observer instance is constantly calling into the Subject class, you’ll have a lot of activity going on for possibly hours on end, keeping the CPU nice and toasty. That’s not a good idea, either.
  • You can perform polling using the observer pattern, which won’t overextend the CPU. In this pattern, the Observer class contains a method called Respond(). Meanwhile, the Subject class includes a list of Observer instances. Further, the Subject class includes a method called Event, which the computer calls whenever something happens, such as a break-in. The application adds and removes Observer instances to and from the Subject’s list of Observer instances, based on the options the people choose when using your application.

As you can imagine, this is a recurring pattern that a lot of applications use. Although zapping a robotic dog might not be commonplace, other applications use this general model. For example, in some C++ editors, you can open the same document in multiple windows, all under one instance of the editor application. When you change the code in one window, you immediately see the change in the other windows. Each class probably has a window, and these windows are the Observer classes. The Subject represents the underlying document.

Defining an observer pattern class

This section discusses how to create an Observer class. The Observer class contains a method called Respond(), which is a purely abstract function in the class declaration — meaning that the derived classes must create their own version of the Respond() function. It’s up to the derived classes to respond to the event in their own ways. The following lines from the AddRemoveItems example (see Listing 4-2, later in the chapter) show how to create the Observer class:

class Observer {
public:
virtual void Respond() = 0;
};

As you can see, there’s not much here, so the example adds some derived classes. Here are a couple:

class Dog : public Observer {
public:
void Respond();
};

class Police : public Observer {
protected:
string name;
public:
Police(string myname) { name = myname; }
void Respond();
};

And here are the Respond() methods for these two classes. For now, to keep it simple, they just write something to the console:

void Dog::Respond() {
cout << "Bark bark" << endl;
}

void Police::Respond() {
cout << name << ": 'Drop the weapon! Now!'" << endl;
}

Again, so far, there’s nothing particularly interesting about this. These lines of code represent just a couple methods that do their thing, really. When the next step rolls around, though, things get exciting. Here’s the Subject class:

class Subject {
protected:
int Count;
Observer *List[100];
public:
Subject() { Count = 0; }
void AddObserver(Observer *Item);
void RemoveObserver(Observer *Item);
void Event();
};

This class has a list of Observer instances in its List member. The Count member is the number of items in the list. Two methods for adding and removing Observer instances are available: AddObserver() and RemoveObserver(). A constructor initializes the list by just setting its count to 0, and there’s the Event() method. Here’s the code for the AddObserver() and RemoveObserver() methods. These functions simply manipulate the arrays:

void Subject::AddObserver(Observer *Item) {
List[Count] = Item;
Count++;
}

void Subject::RemoveObserver(Observer *Item) {
int i;
bool found = false;
for (i=0; i < Count; i++) {
if (!found && List[i] == Item) {
found = true;
List[i] = List[i+1];
}
}
if (found) {
Count--;
}
}

The RemoveObserver() function uses some little tricks (again, a pattern!) to remove the item. It searches through the list until it finds the item; after that, it continues through the list, pulling items back one slot in the array. And finally, if it finds the item, it decreases Count by 1. The Event() method looks like this:

void Subject::Event() {
int i;
for (i=0; i < Count; i++) {
List[i]->Respond();
}
}

This code climbs through the list, calling Respond() for each item in the list. When you put this all together, you can have a main() that sets up these items. Here’s one possibility:

Dog Fido;
Police TJHooker("TJ");
Police JoeFriday("Joe");
Subject Alarm;
Alarm.AddObserver(&Fido);
Alarm.AddObserver(&TJHooker);
Alarm.AddObserver(&JoeFriday);
Alarm.RemoveObserver(&TJHooker);
Alarm.Event();

The code creates three Observer instances (one dog and two cops) and a Subject instance called Alarm. It then adds all three instances to the list; but then TJ Hooker backs out, so the code removes him from the list.

To test the additions, the code calls Event(). (Normally you call Event() when an actual break-in event occurs.) And when you run this code, you get the responses of each of the registered observers:

Bark bark
Joe: 'Drop the weapon! Now!'

Notice that the TJHooker Observer didn’t respond, because it isn’t in the list and didn’t receive a notification. It’s still an instance.

Remember In this example, the three observers (Fido, TJ Hooker, and Joe Friday) are watching the alarm, ready to respond to it. They are observers, ready for action. The alarm is their subject of observation. That’s why the code uses the Observer and Subject pattern.

Observers and the Standard C++ Library

Technical stuff If you’re interested in using templates and the Standard C++ Library, you can make the Subject class a bit more sophisticated by using a list rather than an array. (A list allows you to easily add and remove items without constantly rebuilding the list, as you would need to do with an array.) You can do this by using the standard list class. The only catch is that the list class doesn’t seem to do well with abstract classes. So you need to “de-abstractify” your Observer class, which you do by setting it up like this:

class Observer {
public:
virtual void Respond() {}
};

Then you can modify the Subject class and its methods, like so:

class Subject {
protected:
list<Observer *> OList;
public:
void AddObserver(Observer *Item);
void RemoveObserver(Observer *Item);
void Event();
};

void Subject::AddObserver(Observer *Item) {
OList.push_back(Item);
}

void Subject::RemoveObserver(Observer *Item) {
OList.remove(Item);
}

void Subject::Event() {
list<Observer *>::iterator iter;
for (iter=OList.begin(); iter!=OList.end(); iter++) {
Observer *item = (*iter);
item->Respond();
}
}

Note that the list saves pointers to Observer; not the Observer instances themselves. That’s because, by default, the list class makes a copy of whatever you put in the array. If you put in an actual instance, the list class will make a copy (which creates problems with derived classes because the list copies only the object being stored as an Observer instance, not a class derived from Observer). With pointers, a copy of a pointer still points to the original object, and therefore the items in the list are the originals (at least their addresses are in the list). The list can also add and remove items without needing the program to loop through all the items, as occurs when using an array.

Automatically adding an observer

When you have an application that lets its users configure various observers, you may want to create and delete observers based on the configurations. In that case, it’s possible to add an Observer to a Subject’s list automatically when you create the Observer, and to remove the Observer from the list when you delete the Observer. To do this, you can call the AddObserver() method from within the constructor and call the RemoveObserver() method from within the destructor.

To make this technique work, you need to tell the object who the Subject is by passing the name as a parameter to the constructor. The following code does this. Note that you have to move the Subject class above the Observer class because the Observer’s constructor and destructor call into Subject. Also, note the AddObserver() and RemoveObserver() functions are protected. However, to allow the Observer class to use these functions, you need to add the word friend followed by the word Observer in the Subject class. The code for the complete AddRemoveItems application is in Listing 4-2.

LISTING 4-2: Adding and Removing Items in the Constructor and Destructor

#include <iostream>

using namespace std;

class Observer;

class Subject {
friend class Observer;
protected:
int Count;
Observer *List[100];
void AddObserver(Observer *Item);
void RemoveObserver(Observer *Item);
public:
Subject() { Count = 0; }
void Event();
};

class Observer {
protected:
Subject *subj;
public:
virtual void Respond() = 0;
Observer(Subject *asubj) {
subj = asubj;
subj->AddObserver(this);
}
virtual ~Observer() { subj->RemoveObserver(this); }
};

class Dog : public Observer {
public:
void Respond();
Dog(Subject *asubj) : Observer(asubj) {}
};

class Police : public Observer {
protected:
string name;
public:
Police(Subject *asubj, string myname) :
Observer(asubj) {
name = myname; }
void Respond();
};

void Dog::Respond() {
cout << "Bark bark" << endl;
}

void Police::Respond() {
cout << name << ": 'Drop the weapon! Now!'" << endl;
}
void Subject::AddObserver(Observer *Item) {
List[Count] = Item;
Count++;
}
void Subject::RemoveObserver(Observer *Item) {
int i;
bool found = false;
for (i=0; i < Count; i++) {
if (!found && List[i] == Item) {
found = true;
List[i] = List[i+1];
}
}
if (found) {
Count--;
}
}

void Subject::Event() {
int i;
for (i=0; i < Count; i++) {
List[i]->Respond();
}
}

int main() {
Subject Alarm;
Police *TJHooker = new Police(&Alarm, "TJ");
cout << "TJ on the beat" << endl;
Alarm.Event();
cout << endl;
cout << "TJ off for the day" << endl;
delete TJHooker;
Alarm.Event();
return 0;
}

Notice the Dog(Subject *asubj) : Observer(asubj) {} line of code in the listing. This line tells the application to call the base class constructor first with the subject. This action ensures that the base object, Observer, is correctly instantiated before Dog is instantiated. If you don’t do this, then the instantiation of Dog will fail because Dog won’t have access to the resources in the base class that it needs.

Mediating with a Pattern

The idea behind the mediator pattern is that it performs the work of organizing class communication when you have classes that interact in a complex way. That way, only the underlying mediator class needs to know about all the instances. The instances themselves communicate only with the mediator. The following sections describe the basis for this pattern and demonstrate how it works.

Defining the mediator pattern scenario

Suppose that you’re designing a sophisticated, complex model of a car. You’re going to include the following parts, each of which will have its own class:

  • The engine
  • The electrical supply (for technically minded folks, the battery and alternator combined)
  • The radio
  • The wheels
  • The brakes
  • The headlights
  • The air conditioner
  • The road

Part of your task is to model the behaviors that these classes provide:

  • When the amount of electricity produced changes, the headlights get brighter or dimmer.
  • When the amount of electricity produced increases or decreases, the radio volume increases or decreases.
  • When the engine speed increases or decreases, the amount of electricity produced increases or decreases.
  • When the engine speeds up, the wheels accelerate.
  • When the air conditioner turns on, the amount of electricity available decreases.
  • When the air conditioner turns off, the amount of electricity available increases.
  • When the road angle increases due to going uphill, the speed of the wheels decreases.
  • When the road angle decreases because the car is going downhill, the speed of the wheels increases.
  • When the brakes come on, the speed of the wheels decreases.

This list represents nine objects interacting with each other in different ways. You could try to make all the objects communicate directly with each other. In the code, making them communicate would mean that most of the classes would have to contain references to objects of the other classes. That technique could get pretty confusing.

Figure 4-1 shows a hierarchy of the interactions between classes that demonstrates that you don’t have to have every class communication directly with every other class.

Schematic illustration of a model of the hierarchy between classes.

FIGURE 4-1: A model of the hierarchy between classes.

Outlining the car example

In the example, when there’s a hill, the road angle either increases or decreases, depending on the side of the hill you’re on (uphill or downhill). The road does not need to know about all the other car parts. Instead, it just informs the mediator of the change. The mediator then informs the necessary car parts.

This may seem like overkill because the car parts should be able to talk with each other directly. The idea is that if you enhance this application later, you may want to add more car parts. Rather than connecting the new car part to all the necessary existing car parts, you just make a connection with the mediator object. Suppose that you add a new part called an automatic transmission. When the car begins to climb a hill, the automatic transmission might detect the change in grade and automatically shift to a lower gear, resulting in an increase to the engine speed. To add this class, you only need to define its behavior and specify how it responds to various events, and then hook it up to the mediator. You also modify the mediator so it knows something about the automatic transmission’s behavior. Thus, you don’t need to hook it up to all the other instances. Figure 4-2 shows how the application classes look with the mediator in place.

Schematic illustration of a mediator certainly cleans things up.

FIGURE 4-2: A mediator certainly cleans things up!

One thing not shown in Figure 4-2 (for the purpose of avoiding clutter) is that all the various car parts (including the road!) derive from a base class called CarPart. This class will have a single member: a pointer to a Mediator instance. Each of the car parts, then, will inherit a pointer to the Mediator instance.

The Mediator class has a PartChanged() method. This is the key function: Anytime any of the car parts experiences a change, it calls PartChanged(). Remember that a car part can experience a change in only one of two ways: through an outside force unrelated to the existing classes (such as the driver pushing the gas pedal or turning the steering wheel) or through the Mediator instance. If the change comes from the Mediator instance, it was triggered through one of the other objects. Consider the following steps:

  1. The driver pushes the gas pedal by calling a method in the Engine instance.
  2. The Engine instance changes its speed and then tells the Mediator of the change.
  3. The Mediator instance knows which objects to notify of the change. For this change, it notifies the wheels to spin faster and the amount of electricity produced to increase.

Here’s another possible sequence:

  1. The road has a hill. To tell the car about the hill, the main routine calls a method in the Road instance. The hill has a 10 degree incline.
  2. The Road instance notifies Mediator of the change.
  3. The Mediator instance handles this by figuring out how much to decelerate; it then notifies the wheels to slow down.

So you can see that most of the application smarts are in the Mediator class.

Tip Using the mediator pattern may seem to break the rules for using OOP techniques. The example puts the smarts in the Mediator class. Elsewhere, you may hear that objects must be able to do their own work. But that’s not really a contradiction. In fact, the Mediator class is handling all the smarts dealing with collaborations between objects. After the Mediator instance figures out, for example, that the wheels must spin faster, it notifies the wheels and tells them to spin faster. That’s when the wheels take over and do their thing. At that point, they know how to spin faster without outside help from other classes and objects. So it’s not a contradiction, after all.

Creating the car example

It’s time to put everything you’ve discovered into coded form. The following sections break the car example into manageable pieces, but you need all the pieces before running the example.

Working with the car parts header

The CarParts example begins in Listing 4-3. This is a header file that contains the class declarations for the car parts. Each class provides behaviors appropriate for that part, such as starting and stopping the engine.

LISTING 4-3: Using the carparts.h File

#ifndef CARPARTS_H_INCLUDED
#define CARPARTS_H_INCLUDED

#include "mediator.h"

class CarControls; // forward reference

class CarPart {
protected:
Mediator *mediator;
CarPart(Mediator *med) : mediator(med) {}
void Changed();
};

class Engine : public CarPart {
protected:
friend class Mediator; friend class CarControls;
int RPM;
int Revamount;
public:
Engine(Mediator *med) : CarPart(med),
RPM(0), Revamount(0) {}
void Start();
void PushGasPedal(int amount);
void ReleaseGasPedal(int amount);
void Stop();
};

class Electric : public CarPart {
protected:
friend class Mediator; friend class CarControls;
int Output;
int ChangedBy;
public:
Electric(Mediator *med) : CarPart(med),
Output(0), ChangedBy(0) {}
void ChangeOutputBy(int amount);
};

class Radio : public CarPart {
protected:
friend class Mediator; friend class CarControls;
int Volume;
public:
Radio(Mediator *med) : CarPart(med), Volume(0) {}
void AdjustVolume(int amount) { Volume += amount; }
void SetVolume(int amount) { Volume = amount; }
int GetVolume() { return Volume; }
};

class Wheels : public CarPart {
protected:
friend class Mediator; friend class CarControls;
int Speed;
public:
Wheels(Mediator *med) : CarPart(med), Speed(0) {}
int GetSpeed() { return Speed; }
void Accelerate(int amount);
void Decelerate(int amount);
};

class Brakes : public CarPart {
protected:
friend class Mediator; friend class CarControls;
int Pressure;
public:
Brakes(Mediator *med) : CarPart(med), Pressure(0) {}
void Apply(int amount);
};

class Headlights : public CarPart {
protected:
friend class Mediator; friend class CarControls;
int Brightness;
public:
Headlights(Mediator *med):CarPart(med), Brightness(0) {}
void TurnOn() { Brightness = 100; }
void TurnOff() { Brightness = 0; }
void Adjust(int Amount);
int GetBrightness() { return Brightness; }
};

class AirConditioner : public CarPart {
protected:
friend class Mediator; friend class CarControls;
int Level;
int ChangedBy;
public:
AirConditioner(Mediator *med) : CarPart(med),
Level(0), ChangedBy(0) {}
void TurnOn();
void TurnOff();
bool GetLevel() { return Level; }
void SetLevel(int level);
};

class Road : public CarPart {
protected:
friend class Mediator; friend class CarControls;
int ClimbAngle;
int BumpHeight;
int BumpWhichTire;
public:
Road(Mediator *med) : CarPart(med) {}
void ClimbDescend(int angle);
void Bump(int height, int which);
};

#endif // CARPARTS_H_INCLUDED

These classes know little of each other. That’s a good thing. However, they do know all about the mediator, which is fine. This example uses an important small feature of the American National Standards Institute (ANSI) version of C++. Notice the constructor line in the Engine class definition:

Engine(Mediator *med) : CarPart(med),
RPM(0), Revamount(0) {}

After the constructor definition, you see a colon and the name of the base class, CarPart. This calls the base class constructor. Then there’s a comma and the name of a property (RPM) and a value in parentheses, which together form an initializer. When you create an instance of Engine, the RPM variable will get set to 0. Further, the Revamount variable will also get set to 0. Using the constructor with an initializer causes the constructor to behave just like this code:

Engine(Mediator *med) {
RPM = 0;
Revamount = 0;
}

Working with the mediator and car controls header

In Listing 4-4 you see the header file for the mediator along with a special class called CarControls, which provides a central place through which you can control the car. You may have noticed the CarControls friend class accesses the car parts in carparts.h. This file includes several forward declarations and it knows about the various CarParts classes. This file also includes a Mediator derived class that provides a general interface to the whole system.

LISTING 4-4: Using the mediator.h File

#ifndef MEDIATOR_H_INCLUDED
#define MEDIATOR_H_INCLUDED

// Define all of the required forward references.
class CarPart;
class Engine;
class Electric;
class Radio;
class SteeringWheel;
class Wheels;
class Brakes;
class Headlights;
class AirConditioner;
class Road;

class Mediator {
public:
Engine *MyEngine;
Electric *MyElectric;
Radio *MyRadio;
SteeringWheel *MySteeringWheel;
Wheels *MyWheels;
Brakes *MyBrakes;
Headlights *MyHeadlights;
AirConditioner *MyAirConditioner;
Road *MyRoad;
Mediator();
void PartChanged(CarPart *part);
};

class CarControls : public Mediator {
public:
void StartCar();
void StopCar();
void PushGasPedal(int amount);
void ReleaseGasPedal(int amount);
void PressBrake(int amount);
void Turn(int amount);
void TurnOnRadio();
void TurnOffRadio();
void AdjustRadioVolume(int amount);
void TurnOnHeadlights();
void TurnOffHeadlights();
void ClimbHill(int angle);
void DescendHill(int angle);
void TurnOnAC();
void TurnOffAC();
void AdjustAC(int amount);
int GetSpeed();
CarControls() : Mediator() {}
};

#endif // MEDIATOR_H_INCLUDED

Creating the car parts methods

The methods for all the car parts appear in Listing 4-5. Note that these functions never call the functions in other car parts.

LISTING 4-5: Presenting the carparts.cpp File

#include <iostream>
#include "carparts.h"

using namespace std;

void CarPart::Changed() {
mediator->PartChanged(this);
}

void Engine::Start() {
RPM = 1000;
Changed();
}

void Engine::PushGasPedal(int amount) {
Revamount = amount;
RPM += Revamount;
Changed();
}

void Engine::ReleaseGasPedal(int amount) {
Revamount = amount;
RPM -= Revamount;
Changed();
}

void Engine::Stop() {
RPM = 0;
Revamount = 0;
Changed();
}

void Electric::ChangeOutputBy(int amount) {
Output += amount;
ChangedBy = amount;
Changed();
}

void Wheels::Accelerate(int amount) {
Speed += amount;
Changed();
}

void Wheels::Decelerate(int amount) {
Speed -= amount;
Changed();
}

void Brakes::Apply(int amount) {
Pressure = amount;
Changed();
}

void Headlights::Adjust(int Amount) {
Brightness += Amount;
}

void AirConditioner::TurnOn() {
ChangedBy = 100 - Level;
Level = 100;
Changed();
}

void AirConditioner::TurnOff() {
ChangedBy = 0 - Level;
Level = 0;
Changed();
}

void AirConditioner::SetLevel(int newlevel) {
Level = newlevel;
ChangedBy = newlevel - Level;
Changed();
}

void Road::ClimbDescend(int angle) {
ClimbAngle = angle;
Changed();
}

void Road::Bump(int height, int which) {
BumpHeight = height;
BumpWhichTire = which;
Changed();
}

Remember You can see that each method calls Changed() after each change. This function is in the base class, and it calls into the Mediator’s PartChanged() method, which does all the hard work. Also note that in some of the car parts classes, the Mediator doesn’t respond to their changes (such as the Wheel class), but the methods still call Change(). The reason is that you may add features whereby the Mediator would respond to these changes. Then you won’t have to check to see whether you included a Change() call; it’s already there. This approach helps avoid the problem of wondering why Mediator isn’t doing what it’s supposed to do when the code forgets to call Change().

Creating the mediator and car control methods

Listing 4-6 contains the mediator source code and the source code for the CarControls class. This code appears in mediator.cpp.

LISTING 4-6: Presenting the mediator.cpp File

#include <iostream>
#include "carparts.h"
#include "mediator.h"

using namespace std;

Mediator::Mediator() {
MyEngine = new Engine(this);
MyElectric = new Electric(this);
MyRadio = new Radio(this);
MyWheels = new Wheels(this);
MyBrakes = new Brakes(this);
MyHeadlights = new Headlights(this);
MyAirConditioner = new AirConditioner(this);
MyRoad = new Road(this);
}

void Mediator::PartChanged(CarPart *part) {
if (part == MyEngine) {
if (MyEngine->RPM == 0) {
MyWheels->Speed = 0;
return;
}
if (MyEngine->Revamount == 0) {
return;
}
// If engine increases, increase the electric output
MyElectric->ChangeOutputBy(MyEngine->Revamount / 10);
if (MyEngine->Revamount > 0)
MyWheels->Accelerate(MyEngine->Revamount / 50);
}
else if (part == MyElectric) {
// Dim or brighten the headlights
if (MyHeadlights->Brightness > 0)
MyHeadlights->Adjust(MyElectric->ChangedBy / 20);
if (MyRadio->Volume > 0)
MyRadio->AdjustVolume(MyElectric->ChangedBy / 30);
}
else if (part == MyBrakes)
MyWheels->Decelerate(MyBrakes->Pressure / 5);
else if (part == MyAirConditioner)
MyElectric->ChangeOutputBy(
0 - MyAirConditioner->ChangedBy * 2);
else if (part == MyRoad) {
if (MyRoad->ClimbAngle > 0) {
MyWheels->Decelerate(MyRoad->ClimbAngle * 2);
MyRoad->ClimbAngle = 0;
}
else if (MyRoad->ClimbAngle < 0) {
MyWheels->Accelerate(MyRoad->ClimbAngle * -4);
MyRoad->ClimbAngle = 0;
}
}
}

void CarControls::StartCar() {
MyEngine->Start();
}

void CarControls::StopCar() {
MyEngine->Stop();
}

void CarControls::PushGasPedal(int amount) {
MyEngine->PushGasPedal(amount);
}

void CarControls::ReleaseGasPedal(int amount) {
MyEngine->ReleaseGasPedal(amount);
}

void CarControls::PressBrake(int amount) {
MyBrakes->Apply(amount);
}

void CarControls::TurnOnRadio() {
MyRadio->SetVolume(100);
}

void CarControls::TurnOffRadio() {
MyRadio->SetVolume(0);
}

void CarControls::AdjustRadioVolume(int amount) {
MyRadio->AdjustVolume(amount);
}

void CarControls::TurnOnHeadlights() {
MyHeadlights->TurnOn();
}

void CarControls::TurnOffHeadlights() {
MyHeadlights->TurnOff();
}

void CarControls::ClimbHill(int angle) {
MyRoad->ClimbDescend(angle);
}

void CarControls::DescendHill(int angle) {
MyRoad->ClimbDescend( 0 - angle );
}

int CarControls::GetSpeed() {
return MyWheels->Speed;
}

void CarControls::TurnOnAC() {
MyAirConditioner->TurnOn();
}

void CarControls::TurnOffAC() {
MyAirConditioner->TurnOff();
}

void CarControls::AdjustAC(int amount) {
MyAirConditioner->SetLevel(amount);
}

The CarControls part runs a bit long, but it’s handy because it provides a central interface through which you can operate the car.

Remember The workhorse of the pattern, however, is in the Mediator class. This code consists of a bunch of if statements that look at the change that took place and then call into other classes to modify the objects of the other classes. That’s the whole goal with the mediator pattern: It has a Mediator class containing a general function that looks for changes and then changes other classes.

Driving the car

Now it’s finally time to try the mediator pattern by running the car through its paces. Listing 4-7 shows the various classes in action.

LISTING 4-7: Running the Car through Its Paces

#include <iostream>
#include "mediator.h"
#include "carparts.h"

using namespace std;

int main() {
// Create a new car.
Mediator *MyCar = new Mediator();

// Start the engine.
MyCar->MyEngine->Start();
cout << "Engine Started!" << endl;

// Accelerate.
MyCar->MyWheels->Accelerate(20);
cout << "The car is going: " <<
MyCar->MyWheels->GetSpeed() << endl;

// Apply the brakes.
MyCar->MyBrakes->Apply(20);
cout << "Applying the brakes." << endl;
cout << "The car is going: " <<
MyCar->MyWheels->GetSpeed() << endl;

// Stop the car.
MyCar->MyBrakes->Apply(80);
cout << "Applying the brakes." << endl;
cout << "The car is going: " <<
MyCar->MyWheels->GetSpeed() << endl;

// Shut off the engine.
MyCar->MyEngine->Stop();
cout << "Engine Stopped" << endl;
return 0;
}

The example code performs a few simple tasks using the various classes. You could always add more to your test code. The thing to notice is that everything goes through the Mediator class, MyCar. Here’s the output from this example:

Engine Started!
The car is going: 20
Applying the brakes.
The car is going: 16
Applying the brakes.
The car is going: 0
Engine Stopped