Assignment #4The following problem is presented and discussed in your text under the Observer design pattern.
A local university is designing a system for weather-alert notification that allows students,
faculty, and staff to receive notifications of class cancellations (due to weather) via e-mail, voice
call, or SMS text messages. Other methods of notification may be added in the future. The
system is based on the weather data decision engine that interfaces with several weather-related
data sources, fuses the information, and automatically decides whether class cancellations are in
effect. The university is interested in integrating the existing communication services (i.e., email, SMS, and voice) with the decision engine so that these services can be triggered to initiate
notification via their respective communication types. The design must be flexible so that other
types of communication mechanisms can be added to the system in the future.
Please implement, using an object-oriented language other than C++, this weather-alert
notification system.
What and How to Submit
Please submit a .zip file containing the UML diagram of the detailed design and the source
code of your program.
Software Engineering Design: Theory and Practice
by Carlos E. Otero
CRC Press. (c) 2012. Copying Prohibited.
Reprinted for Personal Account, Drexel University
none@books24x7.com
Reprinted with permission as a subscription benefit of Skillport,
All rights reserved. Reproduction and/or distribution in whole or in part in electronic,paper or other forms
without written permission is prohibited.
Software Engineering Design: Theory and Practice
Chapter 7: Structural and Behavioral Patterns in Detailed Design
CHAPTER OBJECTIVES
Understand the importance and role of structural and behavioral design patterns in detailed design
Identify, understand, and model common structural and behavioral design patterns
Become proficient in implementing models of both structural and behavioral design patterns
Understand the benefits of important structural and behavioral design patterns
CONCEPTUAL OVERVIEW
As seen in the previous chapter, common patterns in object-oriented designs exist to provide detailed design solutions to
problems that recur many times over in different systems. Beside the creational design patterns studied so far, other common
and popular design patterns have been identified to address structural and behavioral problems commonly encountered in
software applications. Structural and behavioral designs patterns help identify problems that deal with the structure and
behavior of software designs; they prescribe the classes required for their design solution and interrelationships required to
support object creation their behavior. These patterns allow designers to quickly and systematically identify structural layouts
of systems (or subsystems) and provide avenues for examining the system’s interactions and quality evaluation within the
operational system. This chapter explores several well-established structural and behavioral design patterns and examines the
problems they are designed to address, together with the benefits they provide. Identifying and designing using these design
patterns can improve the efficiency of the development process and the quality of the final system.
STRUCTURAL DESIGN PATTERNS
Structural design patterns are patterns that deal with designing larger structures from existing classes or objects at run time.
They play a key role in the design and evolution of systems by allowing integration of new designs with existing ones, via
object composition (i.e., object structural) or inheritance (i.e., class structural). Class structural design patterns identify the
inheritance relationship necessary to create new interfaces or implementations that may be compatible with the older design
structure. Object structural patterns provide the relationships required to create larger structures through object composition at
run time, therefore providing more flexibility to extend the system at run time, which is impossible for class structural solutions.
In both cases, by allowing designs to build on other existing structures, systems can be made interoperable by designing
compatible interfaces for otherwise incompatible systems. Structural design patterns have also significant impact on the
reusability and modifiability of systems. Examples of structural design patterns include the adapter, composite, and facade.
ADAPTER
The adapter design pattern is a class/object structural design pattern used to adapt an existing interface to another interface
that is expected in a software system. It can be designed as both class structural, in which the major composition relationships
are defined at compile time, and object structural, where structural object composition occurs at run time. In either case, the
adapter design pattern allows systems with incompatible interfaces to work together, therefore increasing the reusability and
evolution of software systems. According to the Gang of Four (Gamma, Elm, Johnson, and Vlissides 1995, p. 139), the intent of
the adapter is to
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t
otherwise because of incompatible interfaces.
Conceptually, adapters are used everywhere. For example, electrical adapters can be used to connect devices with
incompatible interfaces, such as European plugs and American sockets. In gardening, adapters are used to connect water
hoses with incompatible interfaces to extend their reach. In computer hardware, adapters are used to covert between many
different interfaces, such as serial to Universal Serial Bus (USB). In software, the adapter concept is applied similar to adapters
in the previous examples. Consider the graphical user interface (GUI) for an application to monitor and control a satellite
communication system composed of several independent hardware devices. For each hardware device, complex GUI screens
are designed to monitor and control each device. All screens rely on a class, named HardwareDevice, that provides methods
(e.g., string getStatusA(), string getStatusB()) designed and developed assuming that commands and status for each device
use the string type (in C++). When receiving the hardware devices from their manufacturers and the binary compiled library
that provides monitor and control capabilities for each device, it is noticed that the class ManufacturerHardwareDevice
(provided by the manufacturer) provides all functions for command and control using the char* type or other nonstring type,
Reprinted for BE9S6/hf353, Drexel University
Page 2 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
which creates an incompatible interface between the existing GUI code and the library code. Since the code inside the library
cannot be changed and changing the code for all screens (depending on the number of screens) maybe impractical, the
provided class in the binary library can be adapted using the adapter design pattern to fit the expected interfaces in the GUI
software or vice versa.
Problem
Consider the completed gaming system discussed in Chapter 5, which includes the design and development for all 10 levels of
a gaming system, including the design and implementation of all gaming characters. At each level, the core of the gaming
system (i.e., GameEngine) uses the Character interface to add enemy characters to the game, making them move, defend,
and attack using the moved, defend(), and attack() interface methods, respectively. Each character in the game implements
the Character interface to provide specific behavior appropriate for the character and the level of the game. That is, depending
on the character and the game level, the behavior for moving, defending, and attacking varies among characters. An online
character developer has created a special character that is compatible with the game development’s application programming
interface (API) but not with the particular Character interface; that is, the special character designed by the online developer
includes the following interface methods: specialMove(), specialAttack(), and specialDefend(). The special character is
made available freely to the gaming community; however, the special character code can be downloaded and incorporated into
other gaming systems only as a binary compiled library, which can be incorporated into the existing game. Since all levels of
the game are complete, it is impractical to change the code in all places to detect the new special character and make different
calls for moving, attacking, and defending; therefore, the adapter design pattern is required to adapt the special character’s
interface to the current character interface.
Structure
The general and applied structure of the adapter design pattern is presented in Figure 7.1. As seen, the adapter can be
designed using multiple inheritance or object composition.
Reprinted for BE9S6/hf353, Drexel University
Page 3 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
Figure 7.1: UML class diagram for the class/object structural adapter design pattern
In the multiple inheritance version, the pattern’s relationships between classes and subclasses are defined at compile time, via
inheritance; therefore, this version is classified as class structural. When the adapter design pattern is designed using object
composition, object relationships are defined at run time; therefore, the adapter design pattern in this case is classified as
object structural. The object structural version of the adapter design pattern results in a more dynamic and flexible design;
therefore, it is the recommended and followed approach for the gaming system. The step-by-step approach for designing the
object structural adapter design pattern is presented as follows:
1. Identify the source and destination interfaces that need adapting in the new system (e.g., target and adaptée or character
and SpecialCharacter).
2. Add a new class (e.g., adapter or AdaptedCharacter) in the design that realizes the target interface and implements it in
terms of the adaptee’s implementation. This requires a realization relationship between the adapter and target, and an
association between the adapter and the adaptee.
Reprinted for BE9S6/hf353, Drexel University
Page 4 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
3. In the new system, whenever objects that share the target interface are expected, it can now be possible to use the
adapter objects created in Step 2.
The driving forces for applying the adapter design pattern are reusability and integration of code between two mature software
structures. Under these conditions, changing the code to match either of the two software entities is impractical. Furthermore,
access to the code for either software entity may not be possible; therefore, to reuse and integrate code from one entity to the
other, the adapter is required. When this is the case, the adapter design pattern is easily applied by creating an adapter class
that inherits from the target class. By incorporating new adapter objects that share the Target interface, clients can use them
the same way they used original Target objects; however, these new adapter objects implement the Target functions in terms of
the Adaptée, which is the object providing the new functionality under a different, incompatible interface.
Implementation
The implementation of the object structural adapter design pattern simply requires the creation of one class with two
relationships—realization and association. In practical applications, the Target and Adaptee classes are typically part of larger
mature and sable software structures. In this example, the Target and Adaptee classes are represented by the Character and
BorrowedCharacter classes, as seen in Listings 7.1 and 7.2.
Listing 7.1: C++ Code for the Character Interface
// The Target class.
class Character {
public:
// Interface method for attack functionality.
virtual void attack() = 0;
// Interface method for defend functionality.
virtual void defend() = 0;
// Interface method for moving functionality.
virtual void move() = 0;
};
Listing 7.2: C++ Code for the BorrowedCharacter Interface and ConcreteBorrowedCharacter
// Interface for the borrowed character.
class BorrowedCharacter {
public:
// Interface
virtual void
virtual void
virtual void
};
methods for the borrowed character.
borrowedAttack() = 0;
borrowedDefend() = 0;
borrowedMove() = 0;
// Concrete borrowed character.
class ConcreteBorrowedCharacter : public BorrowedCharacter {
public:
// Implementations for the BorrowedCharacter interface methods.
void borrowedAttack() { /* attack code here… */}
void borrowedDefend() { /* defense code here… */}
void borrowedMove() { /* code to move here… */}
};
As seen, the interfaces for the Character and BorrowedCharacters are different; therefore, client code that expects objects with
the Character interface cannot accept objects with the BorrowedCharacter interface even though conceptually, both objects do
the same operations. To allow the existing code to process objects of the borrowed character type as if they were objects of
the character type, a CharacterAdapter is required, as seen in Listing 7.3.
Listing 7.3: C++ Header File for the CharacterAdapter Class
// Forward reference.
class BorrowedCharacter;
class CharacterAdapter : public Character {
Reprinted for BE9S6/hf353, Drexel University
Page 5 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
public:
// Constructor.
CharacterAdapter(BorrowedCharacter* pCharacter);
// Adapt the attack method.
void attack(void);
// Adapt the defend method.
void defend(void);
// Adapt the move method.
void move(void);
private:
// BorrowedCharacter that needs adapting to the Character interface.
BorrowedCharacter* _borrowedCharacter;
};
As seen, the CharacterAdapter inherits from the Character to implement the realization relationship in C++. It is also
associated with the BorrowedCharacter class via member attribute. To finalize the object adaptation, the newly created
character adapter type is required to implement all interface methods defined by the character interface in terms of the
associated borrowed character, as seen in Listing 7.4.
Once the adapter design pattern is applied, it is easy to use borrowed characters in the gaming system. Consider the method
presented in Listing 7.5 for triggering character behavior in the GameEngine class.
As seen, this and other methods defined in the GameEngine class can now employ the adapter design pattern to accept
character objects that comply with the character interface but provide behavior from the borrowed character. An example of
client code used to activate a borrowed character in the gaming system is presented in Listing 7.6. As seen, the borrowed
character is created and passed into the character adapter during its initialization through the constructor. From this point
forward, the adapted character is used instead of the borrowed character to provide the new features to the game.
Benefits
Allows classes with incompatible interfaces to work together, therefore increasing reusability and ease of code integration
Provides a standard way for integrating a plurality of different types to existing software
Listing 7.4: C++ Source File for the CharacterAdapter Class
#include “CharacterAdapter.
h”
#include “BorrowedCharacter.h”
// Constructor.
CharacterAdapter::CharacterAdapter(BorrowedCharacter* pCharacter) {
// For simplicity, assume a valid pointer.
_borrowedCharacter = pCharacter;
}
// Adapt the attack method.
void CharacterAdapter::attack() {
// Implement the attack functionality in terms of the
// BorrowedCharacter.
_borrowedCharacter->borrowedAttack();
}
// Adapt the defend method.
void CharacterAdapter::CharacterAdapter::defend() {
// Implement the defend functionality in terms of the
// BorrowedCharacter.
_borrowedCharacter->borrowedDefend();
}
// Adapt the move method.
void CharacterAdapter::move() {
// Implement the move functionality in terms of the
Reprinted for BE9S6/hf353, Drexel University
Page 6 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
// BorrowedCharacter.
_borrowedCharacter->borrowedMove();
}
Skill Development 7.1: Adapter Design Pattern
Using the Unified Modeling Language (UML) tool of choice, replicate the UML model presented in Figure 7.1 and generate
code from the model. Using the Integrated Development Environment (IDE) of choice, fill in the gaps in the code generated
using Listings 7.1 through 7.6, and compile and execute the software. Create two additional classes: one that derives from the
character class in which its methods are implemented to simply display some output to the console; and another adapter class
that adapts another character with different interface methods for attacking, defending, and moving (e.g., slowAttack,
slowDefend, and slowMove). Use the triggeredAction method to pass in (one at a time) objects of all the types created.
Observe how the triggeredAction method accepts both objects of the original character interface and the new adapted objects
(since they now all share the character interface). How does the adapter design pattern increase reusability, maintainability,
and modifiability in such system?
Listing 7.5: C++ Code for the GameEngine Method to Trigger a Character’s Actions
class GameEngine {
public:
// …
// Method to activate a character.
void GameEngine::triggeredAction(Character* pCharacter) {
// Activate the character and make it move randomly for a short
// time.
pCharacter->move();
// Once the character stops moving, if being attacked, defend!
pCharacter->defend();
// Once the characters stops defending, if others characters are// detected, attack!
pCharacter->attack();
}
// …
};
Listing 7.6: C++ Code for Client Code in the Gaming System to Activate a Borrowed Character
// Instantiate the game engine.
GameEngine engine;
// Create the borrowed character that needs adapting.
ConcreteBorrowedCharacter borrowedCharacter;
// Create the character adapter and pass in the borrowed character.
// From this point on, the adapterCharacter object can be used
// throughout the game engine as if it were a Character!
CharacterAdapter adaptedCharacter(&borrowedCharacter);
// Move, attack, and defend with the borrowed character’s features!
engine.triggeredAction(&adaptedCharacter);
COMPOSITE
The composite design pattern is an object structural pattern that allows designers to compose (large) tree-like design
structures by strategically structuring objects that share a whole-part relationship. Whole–part relationships are those in which
a larger entity (i.e., the whole) is created by the composition of smaller entities (i.e., the parts). The key advantage of using the
composite design pattern is that it provides a design structure that allows both whole and part objects to be treated uniformly;
therefore, operations that are common to both type of objects can be applied the same way to both types of objects. According
to the Gang of Four (Gamma et al. 1995, p. 163), the intent of the composite is to
Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual
objects and composites of objects uniformly.
Many situations exist that require objects to be composed of many parts. However, in some specific instances, some problems
Reprinted for BE9S6/hf353, Drexel University
Page 7 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
require both objects and their parts to be treated uniformly. In the software domain, perhaps the most common example is seen
in modern user interfaces, which contain both Menu and MenuItem objects. A Menu, in the graphical user interface context, is
a mechanism of the software that allows users to select and activate features of the system. A Menu can also provide access
to other Menus or MenuItems that can be selected to trigger software execution. Both Menu and MenuItem may contain
functions to change their visible text, size, and background color or to handle events. Therefore, both must be treated
uniformly. In this case, the composite design pattern can be used to create a flexible design structure that groups both Menu
and MenuItems, that provides easy addition and removal of both Menu and MenuItems to the design structure, and that
provides a uniform interface so that operations common to both can be easily performed using the composite interface.
Although the composite design pattern is prevalent in examples such as this, its application can be found in numerous practical
applications.
Problem
A wireless sensor system is remotely deployed to collect environmental information. The sensor system communicates via
satellite to a central location, where a schedule of tasks (i.e., a mission plan) is created and sent over satellite communications.
A mission plan is a composite message that contains one or more messages that command the sensor system to perform
particular tasks. These messages contain information on how and when to perform particular tasks. Mission plan messages
can be created with many different combinations of messages. Upon creating the mission plan message, it is sent to the
wireless sensor system, which retrieves each message and message information from the mission plan and executes them to
collect environmental data, store it, and send it back to the central location, as directed by the mission plan message. The
sensor system is extensible and contains many capabilities provided by numerous sensors (e.g., temperature, vibration), stillshot camera, and video recording. To operate the sensor system, the operators at the central location are requesting a
message generator capable of allowing them to easily create a mission plan message. The mission plan message may contain
both primitive and composite messages. Numerous mission plan messages can be created to support different “missions,” and
it is expected that more sensing capabilities will be added in the future. Therefore, the design of the message generator must
provide easy addition and removal of both messages and composite messages to a mission plan. A graphical representation of
the message generator is presented in Figure 7.2.
Figure 7.2: Message generator graphical user interface concept
As seen, the topmost composite message represents the mission plan message (or schedule). The message generator
Reprinted for BE9S6/hf353, Drexel University
Page 8 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
provides a series of menu items that allows operators to select a particular message, to configure its parameters, and to add it
to the mission plan message. In this example, Message 3 is a composite message that can be configured to contain Messages
5, 6, and 7. Message 3 can also be configured individually and added to the mission plan. Both primitive and composite
messages need to be treated uniformly, so that methods such as getId(), setId(), and toXml() can be supported by both
types of messages.
Structure
The general and applied structure of the composite design pattern is presented in Figure 7.3. As seen, the general structure of
the composite design pattern requires three main classes, the Composite, Leaf, and Component classes. The Component
class defines the operations that are common to both Composite and Leaf objects, for example, print(), toXml(), getId(),
setId(), etc., as shown in the Applied View portion of Figure 7.3. The component class also defines the methods specific to
support the composite design pattern, namely, the add() and remove() methods. The add and remove methods are intended
for use by composite objects and not by leaf objects; therefore, they must be specified as overridable methods with default
implementation that indicates an unsupported operation. This means that if at any point during run time a leaf object is called
upon to add or remove objects to the hierarchy, the default implementation for these methods is executed to reflect the
unsupported requests. In many cases, a good alternative for the default implementation involves writing code that throws
exceptions within the add and remove methods. On the other hand, composite objects that derive from the component base
class are required to override the default implementation so that objects can be added and removed from the hierarchy. This
way, when composite objects are bound dynamically at run time to a reference of the component type, the appropriate code for
adding and removing objects is called, preventing the default implementation from being executed. The steps required to apply
the composite design pattern include
1. Identify, understand, and plan the tree-like structure required for the system.
2. With the knowledge from Step 1, identify and design the component base class, which includes overridable methods
common to both leaf and composite objects as well as methods specific to composite objects, which provide capability for
adding and removing objects to the hierarchy.
3. For the methods specified in Step 2 for adding and removing objects to the hierarchy, implement default behavior that if
not overridden will result in an exception or error message indicating an unsupported operation.
4. Identify and design the composite class, which overrides methods for adding and removing objects to the hierarchy. The
composite class requires an internal data structure to store leaf nodes added to the hierarchy. In addition, the composite
class is required to override all other operational methods identified in Step 2 to implement functionality in terms of the
composite object and all of its contained leaf objects.
5. Identify and design the leaf class, which overrides operational methods specified in Step 2 to implement behavior in terms
of the leaf object. Leaf objects do not override the add and remove methods identified in Step 2.
6. Identify and design the client that uses both composite and leaf objects.
Reprinted for BE9S6/hf353, Drexel University
Page 9 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
Figure 7.3: UML class diagram for the composite design pattern
In the message generator example, add() and remove() are used to add and remove both CompositeMessage and all other
derived messages to the design structure (e.g., VideoControlMessage, SelfTestMessage). Therefore, to support the addition of
new messages that provide control of future sensing capabilities, a new class that derives from the Message base class needs
to be added to the message generator design. In addition, to support easy duplication of both types of messages, the prototype
design pattern is incorporated into the message generator design so that message copies can be easily created throughout the
application. This is reflected in the design with the addition of the clone() method to the Message class.
The driving forces behind the design include the ability to treat both individual messages and mission plan messages uniformly,
so that the complexity of client code is minimized. The composite design pattern is also chosen to achieve design flexibility so
that new messages can be created easily to support future systems’ demands. The most influential step when applying the
composite design pattern is the addition of the add() method in the Component base class. Once this method is created,
composite objects override it to store the primitive objects contained by it.
Implementation
Most of the implementation work for the composite design pattern takes place in both the component and composite classes,
which are represented in the message generator by the Message and CompositeMessage classes. Once these are created, other
message classes that share the same Message interface can be added easily by deriving from the Message class. The
implementation for the Message class is presented in Listing 7.7.
As seen, the toXml() and clone() methods are incorporated in the Message type interface. These methods are application
Reprinted for BE9S6/hf353, Drexel University
Page 10 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
specific and are not related to the composite design pattern. The toXml() method is added to enforce the policy that all
messages in the system are required to provide implementation appropriate to convert the particular message data into
Extensible Markup Language (XML) format. Other application-specific methods include the getId(), setId(), getName(), and
setName().
Listing 7.7: C++ Header File for the Message Class
class Message {
public:
// Method to retrieve the message’s id.
int getId() const;
// Method to retrieve the message’s name.
string getName() const;
// Method to set the message’s id.
void setId(int id);
// Method to set the message’s name.
void setName(string name);
// Method to add messages to a composite message.
virtual void add(Message* message);
// Method to display messages to the console.
virtual void print();
// Method to transform the contents of this message to XML format.
virtual string toXml() = 0;
// Duplicate Messages using the prototype design pattern.
virtual Message* clone() = 0;
private:
// The message’s id.
int _id;
// The message’s name.
string _name;
};
The add(Message*) method is specific to the composite design pattern. Specifically, it gives composite classes the ability to add
messages to their structure so that the whole-part relationship can be realized. Because this method is intended specifically for
composite classes, it violates the Liskov substitution principle presented in Chapter 5. However, it provides the functionality
necessary to support efficient solution to the problem; therefore, its usage can be easily justified during design reviews. To
minimize the effects of this violation, careful attention needs to be paid when implementing the default behavior of the
add(Message*) method in the Message base class. The easiest solution is to provide a base implementation that notifies the
operator stating that the operation is not supported. That way, derived classes that do not support the add method would
simply inherit the default implementation. A more sophisticated approach includes writing code that throws an exception
indicating that the operation is not supported. This way, calls to the add(Message*) method from leaf classes will result in
exceptions that can explicitly notify developers of this unsupported operation. The default implementation for both the add()
and print() methods is presented in Listing 7.8. Notice that the print method can be defined in the Message base class to
display the message’s information. This implementation is appropriate for leaf objects but not for composite objects; therefore,
leaf objects can inherit this implementation without further changes.
Listing 7.8: C++ Implementation for the Add() and Print() Methods of the Message Class
#include “Message.h”
#include
#include
// Method to add messages to a composite message.
void Message::add(Message* message) {
// The default implementation lets clients know that the operation
// is unsupported. This behavior is inherited by Leaf classes, but
// overridden by Composite classes.
std::coutprint();
}
}
Benefits
Provides a design structure that supports both composite and primitive objects
Minimizes complexity on clients by shielding them from knowing the operational differences between primitive and
composite objects; clients that expect a primitive object will also work with a composite object, since operations are called
uniformly on both primitive and composite objects
Easy to create and add new component objects to applications
Listing 7.11: C++ Header File for the VideoControlMessage Leaf Class
#include “Message.h”
class VideoControlMessage : public Message
{
public:
// Constructor.
VideoControlMessage(void);
// Destructor.
~VideoControlMessage(void);
// TODO: Specific video control methods.
void setFrameRate(int framesPerSecond);
// .
// .
// .
// Method to transform the contents of this message to XML format.
Reprinted for BE9S6/hf353, Drexel University
Page 13 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
string toXml(void);
// Duplicate the VideoControlMessage using the prototype design
// pattern.
Message* clone(void);
};
FACADE
The facade design pattern is an object structural pattern that provides a simplified interface to complex subsystems. By
providing a simplified interface, the facade design pattern provides a higher level of abstraction that liberates clients from the
responsibility of knowing the internal structure of various elements of the subsystem, which in turn reduces coupling and
simplifies client code. Facade also shields clients from changes that occur in the subsystem; by having a standardized facade
interface, the internal structure of the subsystem can vary without affecting clients. According to the Gang of Four (Gamma et
al. 1995, p. 185), the intent of the facade design pattern is to
Provide a unified to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the
subsystem easier to use.
In most practical applications, components or subsystems are created to abstract a complex behavior that the system must
provide. In these cases, executing a complex subsystem behavior may require the combination of multiple function calls from
multiple component and subsystem elements. For example, consider a software component that provides various elements and
functionality for assessing the integrity of a particular computer. This component may contain elements responsible for
particular items of interests, such as a file system element, memory element, and communication element. In this case, the
facade design pattern can be used to abstract all elements in the subsystem by creating a method testSystem() that in turns
calls upon the various elements of the subsystem in the appropriate order to evaluate the integrity of the system. This allows
clients to be associated only with the facade and relieves them from knowing the internals of the integrity assessment
component.
Listing 7.12: C++ Implementation for the Message Generator and Sample Usage
// Create the initialization primitive messages.
PowerOnMessage powerOnMessage;
SeifTestMessage seifTestMessage;
TransmitStatusMessage transmitStatusMessage;
// The message to task the system to initialize properly.
CompositeMessage initializeTaskingMessage(“Initialize System”);
// Add copies of the power on, self test, and transmit status messages
// to the initialize tasking composite message.
initializeTaskingMessage.add( powerOnMessage.clone() );
initializeTaskingMessage.add( selfTestMessage.clone() );
initializeTaskingMessage.add( transmitStatusMessage.clone() );
// Collection Control Messages.
TemperatureSensorControlMessage temperatureSensorControlMessage;
VideoControlMessage videoControlMessage;
// The message to task the system to collect information.
CompositeMessage collectionMessage(“Information Collection”);
// Add the temp, sensor and video control messages to the collection
// tasking composite message.
collectionMessage.add( temperatureSensorControlMessage.clone() );
collectionMessage.add( videoControlMessage.clone() );
// Shutdown Messages.
ShutdownMessage shutdownMessage;
// The message to task the system to complete Mission 1.
CompositeMessage missionPlanMessage(“Mission 1 – Temperature
and Video Collection”);
// Add the messages to the initialize, collection, and shutdown
// messages to the mission plan composite message.
missionPlanMessage.add( initializeTaskingMessage.clone() );
missionPlanMessage.add( collectionMessage.clone() );
missionPlanMessage.add( shutdownMessage.clone() );
// Before sending message, verify its content.
Reprinted for BE9S6/hf353, Drexel University
Page 14 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
missionPlanMessage.print();
//
//
//
//
If content is valid, send the message through the system. Before
being sent out through the communication link, a call to
missionPlanMessage.toXml() is made to convert all of the
message’s content to XML format.
Listing 7.13: Sample Output for the Message Generator Problem
Composite Message: Mission 1 – Temperature and Video Collection, Id: 20
Composite Message: Initialize System, Id: 20
Message Power On Message, Id: 0
Message Self Test Message, Id: 1
Message Transmit Status Message, Id: 2
Composite Message: Information Collection, Id: 20
Message Temperature Sensor Control Message, Id: 3
Message Video Control Message, Id: 4
Message Shutdown Message, Id: 5
Press any key to continue …
Problem
Consider the sensor system described as part of the message generator in the previous section. Upon field deployment, it is
desirable to test the system’s capabilities to ensure that the system works properly before engaging in autonomous operation.
For this reason, a graphical user interface is required to monitor and control the system in the field during installation. A
conceptual diagram of both subsystems and their interactions is presented in Figure 7.4.
Figure 7.4: Conceptual diagram of wireless sensor system
The sensor subsystem consists of the following elements: SystemManager, SerialComm, FileSystem, DataAnalyzer, and
WirelessComm. A typical set of operations to assess the integrity of the system would require clients to know about all
subsystem elements—for example, opening the serial port, sending a collection message to the system manager, and opening
the wireless communication link. This adds complexity to the developers of the UI subsystem, since they are required to know
the details of the sensor subsystem. In addition, in many practical applications, the internals of subsystem are prone to change;
therefore, clients relying on the internal of subsystems must keep up with changes throughout development and maintenance
phases. The designers of the sensor system wants an easy solution to shield UI subsystem developers from changes in the
sensor subsystem—one that allows developers of the sensor subsystem to identify and set interfaces for the visible aspects of
Reprinted for BE9S6/hf353, Drexel University
Page 15 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
the sensor subsystem so that clients can rely on this interface, giving developers of the sensor subsystem the ability to change
the internals of the subsystem without affecting clients.
Structure
The general and applied structure of the facade design pattern is presented in Figure 7.5. As seen, the facade provides
functionality in terms of existing classes through a simplified interface. In most cases, a facade’s functions will be implemented
in terms of several functions from different subsystem classes. This prevents clients from depending on these internal
subsystems; therefore, both complexity and dependencies on the client side are reduced.
Figure 7.5: UML class diagram for the facade design pattern
Consider the set of operations required to retrieve sensor data, which may require opening communication links, testing the
connections, and scheduling a collection message. These operations require interfacing with several components within the
sensor subsystem. In such cases, the UI subsystem is required to know the details required to carry out all of these operations,
which increases coupling and complexity of the UI. However, with the facade design pattern, an interface method, named
transmitSensorData(), can be used to abstract all of the required operations to transmit sensor data, such as opening the
serial connection, opening the wireless connection, testing both connections, and scheduling a collection message. A step-bystep procedure for applying the facade design pattern includes
1. Identify all components involved in carrying out a subsystem operation.
2. Create an ordered list of the operations required to execute the subsystem operation.
3. Design a facade class that includes an interface method to carry out the subsystem operation. The facade class has
dependencies to all other subsystem components required to carry out the subsystem operation.
4. Implement the facade interface method by calling operations on one or more subsystem components, in the order
identified in Step 2.
Reprinted for BE9S6/hf353, Drexel University
Page 16 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
5. Allow one or more clients to access the objects of the facade type so that they can gain access to the subsystem
operation. This creates a many-to-one relationship between external subsystems and the facade interface instead of
many-to-many relationships.
The driving forces behind the facade are simplicity of client code and lower coupling. The facade design pattern can be simply
applied by creating an additional facade class that provides simple functions summarizing the major external functions
expected and required by clients.
Listing 7.14: C++ Implementation of One Method of the SensorSystem Facade
void SensorSystem::transmitSensorData(int sensorNumber) {
// Create an object for serial communications parameters.
SerialParams params;
params.setCommPort( SerialParams::COM_1 );
params.setBaudRate( SerialParams::BR_9600 );
params.setParity( SerialParams::PARITY_NO_PARITY );
params.setByteSize( SerialParams::BYTE_SIZE_8 );
params.setStopBits( SerialParams::STOP_BIT_ONE );
// Retrieve pointer to the serial communication object.
SerialComm* pSerialComm = SerialCoram::getInstance();
// Open the serial communication with the specified parameters.
if( serialComm->open(params) ) {
// Ready to communicate with collection nodes, now get ready for
// transmitting the data via the wireless link.
TcpConnection* pConnection = TcpConnection::getInstance();
if( pConnection->open(TcpConnection::PORT_NUMBER,
TcpConnection::IP_ADDRESS) ) {
// Schedule a collection message.
SystemManager::getInstance()->scheduleMessage(/*…*/);
}
else
//
//
} //
{
Log TCP error here.
Close serial connection.
end if( pConnection->open(…)
}
else {
// Log serial connection error here.
} // end if( serialComm->open(…)
} // end transmitSensorData function.
Implementation
Implementing the facade design pattern is straightforward, since it simply provides behavior in terms of other subsystems. An
example facade method for transmitting sensor data is presented in Listing 7.14. As seen, many of the complexities associated
with using the subsystem elements are hidden by the facade. By depending only on the facade, clients are shielded from
unnecessary details required to perform the operation.
Benefits
Shields clients from knowing the internals of complex subsystem, therefore minimizing complexity in clients
Since the internals of the subsystem are prone to change, provides a stable interface that hides changes to internal
subsystems, therefore making client code more stable
Promotes weak coupling on clients; clients depend on only one interface instead of multiple interfaces
BEHAVIORAL DESIGN PATTERNS
Behavioral design patterns deal with encapsulating behavior with objects, assigning responsibility, and managing object
cooperation when achieving common tasks (Gamma et al. 1995). Behavioral design patterns include many of the mainstream
design patterns used in modern object-oriented frameworks and play a key role in the design of systems by making them
independent of specific behavior, which is made replaceable with objects throughout these design patterns. Therefore, parts of
Reprinted for BE9S6/hf353, Drexel University
Page 17 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
the system responsible for performing some algorithm or behavior do so by relying on a common interface without knowledge
of how the actual behavior or algorithm is carried out. In addition, by controlling the behavioral process with common interfaces,
enforcing behavioral policies becomes easier, therefore giving systems the ability to create algorithms that share a common
interface but that vary widely in behavior. Examples of behavioral patterns include the iterator and the observer.
ITERATOR
The iterator design pattern is an object behavioral pattern that provides a standardized way for accessing and traversing
objects in a collection data structure. A collection data structure may consist of arrays, vectors, lists, or other custom-designed
structures. The iterator design pattern works by abstracting the way each specific collection structure operates on the data so
that clients are not required to have knowledge of the details of their internal structure. According to the Gang of Four (Gamma
et al. 1995, p. 257), the intent of the iterator design pattern is to
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying
representation.
By providing a standard interface and encapsulated methodology for accessing elements of a collection structure, client code
becomes more consistent and easier to maintain, since changing the internal structure of the data collection structure does not
affect the way client code interacts with the structure. Iterators are prevalent in software engineering; their presence can be
found built-in in today’s most common programming languages and frameworks, such as C++, Java, and the .NET framework.
Problem
A company’s software system manages inventory, financiais, and all other information available from its two store branches.
Each store carries specific computer products appropriate for its location’s demographics. During design, the software system
is decomposed into several components, including two components for deferring and abstracting design information relevant to
requirements for each computer store branch. The detailed design of each component is carried out separately by two different
software engineers; this results in two different versions of data structures for managing and providing store product
information. Now, anytime the software system is called upon to display information about store products, it is required to
identify between the two store branches so that the correct implementation for accessing store information can be executed.
This problem is encountered every time a new computer store branch is added to the system; therefore, a uniform and
standardized method for accessing computer store products from different collection data structures is highly desirable.
Consider the existing ComputerProduct code for the store’s software system, as presented in Listing 7.15. The
ComputerProduct is the product class for all products carried at all store branches, which includes simple or advanced
computer products.
Consider the case where the designer of one computer store branch uses a list data structure to save computer products for
the computer store branch carrying simple computer products. To retrieve the products from the simple computer store, a
method is provided, getProducts(), which returns a reference or pointer to the object of type SimpleProductList, as seen in
Listing 7.16.
Listing 7.15: C++ Specification of the ComputerProduct Type
class ComputerProduct {
public:
// Return the product’s id.
int getProductId();
// Return the product’s price.
int getPrice() const;
// Return the product’s description.
string getDescription() const;
// Other methods here…
};
Listing 7.16: C++ Code for the SimpleComputerStore Class
#include “SimpleProductList.h”
// Simple Computer Store
class SimpleComputerStore {
Reprinted for BE9S6/hf353, Drexel University
Page 18 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
public:
// Constructor.
SimpleComputerStore() { /*Connect to DB and initialize all
products.*/ }
// Computer store methods…
// Return the computer products carried by the simple computer store.
SimpleProductList* getProducts() {
// Return the simple product list.
return &_products;
}
private:
// The list of simple computer products.
SimpleProductList _products;
};
The second computer store, which carries advanced computer products, is designed to keep track of computer products using
a custom-made list data structure for the advanced computer products class. This list provides a method getProducts() that
returns a reference or pointer to a collection object of type AdvancedProductList, as seen in Listing 7.17.
As trivial as this problem may seem, it highlights problems in code that occur typically in software teams during practical
applications. Different developers want to work with their own code, they may want to showcase their skills by developing a
better collection data structure, or the lack of oversight in the design process results in work redundancy. This example
presents the problem using two different (but almost similar) lists; however, in practical situations, the difference in design and
implementation may involve significantly different approaches like arrays versus custom-defined lists versus library-specific
lists, such as the C++ standard template library list. Lack of standardization in the way that computer stores access and
traverse through their products creates complexity for clients. Consider the software system code for displaying computer
product information from both stores, as presented in Listing 7.18.
Listing 7.17: C++ Code for the AdvancedComputerStore Class
#include “AdvancedProductList.h”
// Advanced Computer Store
class AdvancedComputerStore {
public:
// Constructor.
AdvancedComputerStore() { /*Initialize all products.*/ }
// Computer store methods…
// Return a pointer to the advanced product list.
AdvancedProductList* getProducts() {
// Return the advanced product list.
return &_products;
}
private:
// The computer products… in ProductList form.
AdvancedProductList _products;
};
Notice that by having different methods to retrieve each product (i.e., getSimpleProduct and getAdvancedProduct), the client
code now requires a conditional statement to differentiate between the two store branches, which results in two versions of
code for displaying product information. For each store added to the system, a new conditional statement is required to support
the display of the new computer store branch.
Structure
The general and applied structure of the iterator design pattern is presented in Figure 7.6. The key to designing the iterator
design pattern lies in the Iterator interface. As seen in the General View portion of Figure 7.6, the Iterator interface consists
of the first(), next(), isDone(), and currentItem() interface methods. These methods specify the fundamental operations
that need to be provided by iterator objects that implement the interface. Regardless of the collection data structure employed
Reprinted for BE9S6/hf353, Drexel University
Page 19 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
to store products, these interface methods can be used uniformly to traverse the items contained by the data structure. The
first() method is used to return the first item in the collection; the next() method is used to move the current item to the next
element of the list; the currentItem() method is used to return the product stored at the current location of the iterator; and the
isDone() method is used to determine if there are more products to traverse in the collection item.
Figure 7.6: UML class diagram for the iterator design pattern
Listing 7.18: C++ Code for the Centralized Server Software to Display Computer Products
// Simple store.
SimpleComputerStore simpleStore;
ComputerProduct* pProduct = 0;
SimpleProductList* simpleStoreProducts = simpleStore.getProducts();
// Display simple store products.
for( int i = 0; i < simpleStoreProducts->size(); i++ ) {
// Retrieve the product at index i.
Reprinted for BE9S6/hf353, Drexel University
Page 20 of 30
CRC Press, Taylor & Francis Group, LLC (c) 2012, Copying Prohibited
Software Engineering Design: Theory and Practice
pProduct = simpleStoreProducts->getSimpleProduct(i);
// Make sure pProduct is valid before using it!
// Display product’s information.
cout