Abstract

Design Patterns, Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides [also known as the Gang of Four (GOF)] has been a de facto reference for any Object-Oriented software developer. This article is Part V of a series of articles illustrating the GOF Design Patterns in C#. We will discuss the Iterator, Mediator, Memento, Observer, and State patterns. The next article will conclude the C# illustrations of the GOF design patterns as well as provide some closing remarks. It is assumed the reader is familiar with basic C# syntax and conventions, and not necessarily details of the .NET Framework.

Background

In Design Patterns, each pattern is described with its name (and other well-known names); the motivation behind the pattern; its applicability; the structure of the pattern; class/object participants; participant collaborations; pattern consequences; implementation; sample code; known uses; and related patterns. This article will only give a brief overview of the pattern, and an illustration of the pattern in C#.

A design pattern is not code, per se, but a "plan of attack" for solving a common software development problem. The GOF had distilled the design patterns in their book into three main subject areas: Creational, Structural, and Behavioral. This article deals with the Behavioral design patterns, or how objects act together. The first three articles in this series dealt with the Creational and Structural patterns, and the last article introduced the Behavioral patterns. This article continues the illustration of the Behavioral patterns as described by the Gang of Four.

This article is meant to illustrate the design patterns as a supplement to their material. It is recommended that you are familiar with the various terms and object diagram methods used to describe the design patterns as used by the GOF. If you're not familiar with the diagrams, they should be somewhat self-explanatory once viewed. The most important terms to get your head around are abstract and concrete. The former is a description and not an implementation, while the latter is the actual implementation. In C#, this means an abstract class is an interface, and the concrete class implements that interface.

Behavioral Patterns

To quote the GOF, "Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects. Behavioral patterns describe not just patterns of objects or classes but also the patterns of communication between them. These patterns characterize complex control flow that's difficult to follow at run-time. They shift your focus away from flow of control to let you concentrate just on the way objects are interconnected."

Iterator

The intent of the Iterator pattern according to the GOF is to "Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation." C# is already rife with iterators, so I am only going to give an overview. The structure looks like this:

Many of the .NET System.Collections assembly classes support all the functionality of the Iterator pattern through a call to GetEnumerator(), which returns a concrete iterator, the IEnumerator interface. I will not illustrate usage of the .NET "iterator," for there are plenty of examples that abound.

Mediator

When designing systems, a problem often occurs when there are many objects making many calls to each other. There is a great potential for things to get "lost in the shuffle." This is especially true months or years down the road when systems get refactored / updated. Things break rather easily because things are tightly coupled. The Mediator pattern is useful because it allows you to keep objects from referring to each other directly, and "it lets you vary their interaction independently." (GOF) The structure of the Mediator pattern looks like this:

For our illustration (mediator.cs,) suppose we consider an aviation manufacturer's robot control process. There are several robots, which report activity, errors, need for activity, etc. The robots need to communicate with others in order to change their state, for example, if an operational error occurs, or a subsystem has been completed. Because of the various number of robots and unrelated messages, the current system has become quite ugly to maintain. The Mediator pattern allows us to simplify the interactions. We'll define our base class robot, which knows it's "director":

The responsibility of inter-object communication has been moved to the director, which in turn mediates the object interaction. Objects involved need not "know" how to interact with other objects in the same director's arena, only that they need to communicate with the director. This allows a great deal of flexibility.

Memento

Object state is a tricky thing in software development. An object needs to save or retrieve its state, but in doing so may expose itself, revealing implementation details and breaking encapsulation. The Memento pattern is useful in that it stores a snapshot of state from its originator, and only the originator can directly access the stored state. The memento is opaque to other objects. The pattern structure:

The Caretaker knows nothing except how to store and manage Memento objects. The Originator can access or create a Memento to preserve its state. The Memento can contain as much or as little state as required by the Originator. Using this pattern, we can assign (recall) arbitrary states of an object. Unfortunately, in C#, there is no concept of friend access as there is in C++, so opacity to other objects is unreachable. A separate state object in C# must expose methods to the originator, and in doing so, it exposes these methods to all other objects. I will not illustrate this pattern due to this limitation. One could declare the state member fields or properties as internal, but the members would still be exposed to all objects within a program.

Observer

Object state changes, and it is often necessary for other objects to be notified when this happens. The Observer behavioral design pattern is used for just that. Imagine a data structure which can be represented by several graphical views, when the data structure changes, so must the views. The pattern looks like this:

C# is well-suited for this pattern because of delegates and events, inherently supporting the Observer pattern. For our example (observer.cs), a business has a purchase order editing system has several objects which watch for price changes on various products. Some of these objects may be user interface, some may be business objects which operate on open purchase orders. We define the delegate for the price change:

publicdelegatevoid PriceChanged(BaseProduct p);

In our base product class, we define the "observer" storage for event notification:

publicevent PriceChanged PriceChangedObservers;

We add notification of the observers to the price property of our base product:

State

Objects often change their behavior when their internal state changes. Rather than using a plethora of conditional statements, the State pattern allows "plug in" behavior. For example, a vending machine may allow purchase and dispensing of an item depending on how much money has been given. The State pattern structure looks like this:

For our illustration (state.cs), a business uses an Order object which can load order details "on the fly." Calculation of order totals depends on whether or not the details are loaded. We use the State pattern to define the behavior of this calculation.

We store a reference to a "base order state" in our Order context, and delegate order operations to the reference:

Notice that we allow "just in time" loading of order details in the Order Context, while the State can perform this JIT loading, it's more desirable to have the Context test the state for whether or not the state "is loaded."

Our BaseOrderState defines the operations whose behavior changes according to state, and our derived classes implement the proper behavior, including changing the state of the OrderContext:

Conclusions

Behavioral design patterns allow for great flexibility in how your software behaves:

The Iterator pattern simplifies the traversal of collections.

The Mediator pattern allows you to simplify interactions between large numbers of objects, decoupling them from each other; promotes better object reuse and customization by reducing explicit "links" to other objects. It also reduces subclassing and simplifies object protocols, easing maintenance; abstracts how objects communicate letting you focus on how objects interact independent from their behavior apart; and centralizes control.

The Memento pattern is useful in languages (such as C++) where the concept of friend access is supported, in order to provide a mechanism to store object state without breaking encapsulation rules.

The Observer pattern is powerful and easy to implement in C# compared to other languages because of the inherent support for delegates and events. It is easy to observe many subjects by adding event handlers to the observers. There are nuances to this pattern which you should review in the GOF Design Patterns book, such as ensuring the subject's state is consistent, or where the notification is triggered. The Observer pattern allows abstraction of subject/observer coupling and broadcast communication. A downside is that notifications may send a deluge of updates to observers, which is why you should use a well-defined notification event and handler instead of a broad "subject changed" notification.

The State pattern allows localization of state-specific behavior, allowing for easy addition of new states and transitions by adding subclasses. The transitions become explicit, and protects the context from inconsistent states because the transitions are atomic from the point of view of the context. The state objects can also be shared amongst contexts. The drawback is that you may end up with a lot of classes.

Stay tuned for future articles...

Building the Samples

Unzip the source files to the folder of your choice. Start a shell (cmd.exe) and type nmake. You may have to alter the Makefile to point to the correct folder where your .NET Framework libraries exist.

Share

About the Author

Project Manager and Software Architect with 20+ years of experience in the software development field. Broad experience in delivering robust and scalable information solutions in a variety of horizontal and vertical markets including media, finance, supply chain and health care.

Specialties

Current emphasis on Digital Content Solutions, Service Orientation, User Experience (UX) and Distributed Large Scale Software Patterns. Experience in managing teams of developers and enforcing compliance with industry standards.

Comments and Discussions

Great article!!!
Memento pattern says...without violating encapsulation. How to achieve this in c#? Can this be done using private setter and passing memento object in caretaker constructor?
Thank you.

Without adieu, the chattering birds lifted off the grass in unisonThe sun was obscured - just a little -by a few clouds, skirted in avoidance by the fresh aviatorsSoft, muted dreams cause tiny motions in sleep, rising.Comfort in your arms, the clouds liftNight falls and encompasses us in its ebon sheath, the chattering birds long goneAnd us in delicate flight, slumberingly aware, of the universal vista, patterned beneath.