Design Patterns: Getting things done (1) Managing processes with the Mediator and State Pattern

Posted on July 30, 2011

0


In this post and the next series of posts we will look at 6 patterns to get thing done.

The Mediator and State Pattern

This post will focus on the Mediator and State Pattern and provides you a brief overview of the similarities and differences in use and approach.

To improve some of the shortcomings of the State Pattern (centralized management of State) I also briefly go into two different additonal “patterns” called the: “State Maanger” and: “State Machine”.

Next posts

Next posts will go into the Strategy and (Simple Factory) and the Builder and Interpreter and Decorator Patterns.

Overview

I separate the patterns “to get things done” in three different groups, based on the use. A fourth group contains supporting patterns that help you refine specific strategies in the first three categories.

Each of the first three groups are patterns to offer you a context specific and dynamic way to deal with specific situations. It allows you to move from “single option processing” to “multiple option processing”:

  1. How do you want to manage and handle (user/system based) actions? – Depending on the state of (a part of) your application, the type of response to a specific action changes. A simple example: when you are saving a file, pressing “Save” again should result in no action at all.
    1. Mediator Pattern – Use when you need to manage complex processes. Can be used as an logical alternative for a State Pattern.
    2. State Pattern – Use when the behavior of (a part of) your application changes based on the state (of a part of) your application.
  2. How do you want to process stuff? – Where the Mediator and State Pattern are used to “manage” processes based on (business) logic, Strategy and (Simple) Factory are mostly used to offer dynamic ways to handle stuff and produce context-specific objects.
    1.  Strategy Pattern – Use when you have several ways to solve one problem (your strategy) and you want an easy way to choose that strategy dynamically
    2. (Simple) Factory – Use when you want to create specific objects of a specific type. The (Simple) Factory is/seems very similar to the Strategy Pattern, with this different: the intention of the Strategy Pattern is to select one out of multiple ways (strategies) to solve a problem.
  3. How do you want to construct (composite) objects? – The area where I find most use of the Builder and Interpreter Pattern is in building dynamic/configurable Graphical User Interfaces based on XML input. Each GUI is in the basis a Composite of nested objects (Forms, containing fields, buttons and checkboxes for instance). Using a Builder or Interpreter and (XML) input allows you to “inject” any GUI you want and need.
    1. Builder Pattern – The Builder Pattern creates a dynamic (object) model based on rules or fixed patterns. It is most similar to a kid using LEGO.
    2. Interpreter Pattern – Uses clear text instructions as a basis to construct composite objects or handle data in specific ways. Parsing HTML code into a working web page can be a simple example of an Interpreter.
  4. Supporting patterns
    1. Decorator Pattern – Encapsulates objects to extend their possibilities. While on the edge of: “does it really belong in this list?”, the way you can use the Decorator Pattern says “yes”.
    2. Composite Pattern – In the context of this series I consider the Composite Pattern as a possible tool for two other Patterns: Interpreter and Builder Pattern.
    3. State Machine – The State Machine (see this WikiPedia entry on the software implementation) can help you fix one of the possible weaknesses (depending on your implementation) of the State Pattern: that management and state changes are localized in each State Handler in the State Pattern

Mapping it

Result and Object based

What do you need: Results or Objects? – a separation

When you look at your needs: what is it that will drive the solution to your problem?
  1. You need a Result – For instance: parsing XML to Objects, or a specific behavior in a specific state of (a part of) your application. Both State and Strategy can help you, where State is more focused on Actions and Strategy more suited to process or handle stuff.
  2. You need Objects – for instance: to present something, or to deal with specific situations. You will find these – and similar – patterns a lot when you work with front-end code.

Brief on State and Mediator: when do you need them?

A uses case: saving a file

Imagine the example given earlier: where you have an application that saves a file to a specific location. The action of saving a file has different states:

  1. Not saved, not changed – File is not saved (yet), but the user did not change anything
  2. Not saved, changed – File is not savedm but the user (or the system) changed something
  3. Saving – The system is saving the file
  4. Saved – The system has saved the file
While saving, you want to implement the following rules:
  1. The user can not repeat the Save request while the file is saving – To avoid issues due the the fact that a requested process is already running, you either want to
    1. Postpone the request – Until the previous one is done
    2. Ignore the request – As the system is blocking anyway and no supplemental changes are done, or because you will request the user to try again when saving is done
  2. The user can make successive edits while the file is saving – As:
    1. The process is asynchronous – And can take several seconds to finish as the file might be saved over a network connection
    2. You want a non-blocking system – To allow the user to “work while waiting”
    3. The file that is saved is an isolated copy – Of the file to be saved and additional changes do not corrupt the data you are saving at the same time

For each State (Not saved/not changed, Not saved/changed, etcetera) you have different responses to the same user action.

Additionally, you might want to add an additional set of states to respond to, to assure the best response, based on the situation the user is in. For instance:

  1. Remote location, fast connection – When the user saves the file, it is done in seconds
  2. Remote location, slow connection – When the user saves the file, it can take minutes
  3. Local storage, fast – When the user saves the file, it is done in seconds
  4. Local storage, slow – (Like a pen drive.) When the user saves the file, it can take minutes

It might be that you want to incorporate the situational states (fast or slow storage, remote and local) into your decision-making process as well. And it might be that you want to change your strategy half way through the process as your original line of thought when you started turns out to be wrong when you test it.

If your first choice is to solve this case with “If/Then/Else” statements, inline code and method calls to do the actual work, you are on your way to create a State or Mediator Patterns.

State and Mediator Pattern to the rescue

The State and Mediator Pattern help you cut this case into clear parts and a clear pattern.

Each of these two patterns has a specific approach to it

State and Mediator differ mostly in the way they manage State and State Specific Actions:

  1. State: Internal resolve and Distributed Control – The State Pattern puts the responsibility of managing the (next) State into the hands of each individual State Handler. As there can be many State Handlers each capable of- and allowed to decide what the next , management is distributed over the State Handlers themselves. The State Handlers in most implementations of a State Pattern (at least in the text book examples) resolve the management of the process internally.
  2. Mediator: Delegation and Centralized Control – Instead the Mediator manages the state of an application centrally and (usually) delegates the real implementation of the state related actions to separate objects, handlers or classes. These handlers should not be allowed to decide what step is next, nor should they be allowed to manipulate any data in the Mediator. To keep control over all processes (and to avoid stuff from happening somewhere, derailing stuff elsewhere) the Mediator should never allow other components to “Mediate” as well.

Summarizing

The State and Mediator Pattern are most valuable in situations where you have:

  1. Context specific responses to actions, based on the state of (a part of) the application – Where the state of (a part of) an application can be: “user is logged in/out”, “user is looking at tab 1 of the application”, “user is filling in part one of a form”, “user pressed on submit”, “system is unable to connect to the database” and so on. And where the response might be: hiding and showing elements the user does not- and does need at that stage of the application, blocking specific actions until the state changes to one where those actions are allowed, removing or moving or adding elements based on drag and drop or click actions (think visual editors).
  2. Bloated classes due to situation specific responses – Where specific actions based on If/then/else becomes start to bloat your classes towards 500 lines of code and more.

Changing the color of your screen – a simple use case for a code example

Lets say you have a button that flips the color of your screen through three different colors in a fixed sequence:

  1. From Red
  2. To Green
  3. To Blue
  4. To Red again (and the cycle repeats)

Each color presented can be seen as a “state”.

Let’s assume further that each State has a very specific next step. I can only move from Red to Green to Blue to Red.

Using the State Pattern – localizing decision making in State Handlers

In the State Pattern, each State is represented in a specific object, that holds all logic and data for that specific state.

For instance (pseudo-code):

class ColorState
{     
      // We inject the next state via the Environment
      public var nextState: ColorState;
      private var environment:Environment;
      private var color:uint;
      public method ColorState(environment, color)
      {
            // Set environment. so we can pass next state
            this.environment=environment;
            // set Color
            this.color=color
      }
      public method setColor()
      {
              environment.backgroundColor=this.color;
             // Inject next state so next time the user presses button,
             // the next state will handle the color
              environment.state=this.nextState;
      }
}

Our main Class might look something like this:

class Environment
{
       private var redState, greenState, blueState : ColorState;
       private var currentState :ColorState; 
       public Environment()
       { 
              // Define the states
              blueState=new ColorState (this,#0000ff);
              redState=new ColorState(this,#ff0000); 
              greenState=new ColorState(this,#00ff00);
              // Define the next state - this is to illustrate
              blueState.nextState=redState;
              redState.nextState= greenState;
              greenState.nextState=blueState; 
              // Set current state - this will set the color and inject the next state
              currentState =blueState
              // Activate the state to start the process
              state.setColor();
              // Set the button click handler
              colorChangeButton.onClick=onButtonClick; 
       }
       public method onButtonClick(evt:Event)
       {
               // As our state objects deal with all state specific administration,
               // we just call the method and trust our State Object to deal with the rest
               currentState.setColor();
               // Done
         }
}

In this very simple example we define the different states when we initialize the Environment.

Each time the user clicks the Color Change Button, the State Objects will determine what to do on a specific method call (in our case only one) and if a new state needs to be injected.

In this example we also used only one class for our State Object, as we only needed to do two things:

  1. Inject the next state – Into our Environment
  2. Change the color – Of our screen
It is possible that your State Objects are created from several different Classes, as the behaviors per State are very different. In that case, using Inheritance and implementing Interfaces are your first actions per class.

Two things I do not like on this textbook example

  1. We have a double reference between Environment and the State Handler – The State Handler references to the Environment and the Environment references to a (the current) State Handler. Which is a form of tight coupling that can be avoided and when avoided leads to cleaner and more agile code.
  2. We store (a part of) the State of the Environment in the Environment itself – Which seems OK when it is only one state per Environment. Unfortunately, things are seldom that simple. In practice, this approach leads to an increasing mess when more and more states for specific situations (think of specific states hiding and showing specific buttons) are added. Better is to externalize all state-specific stuff to a separate class that then manages the object you manage the state of. See: “Using State Machines” further down this post.

The Mediator Pattern – centralizing decision making

The Mediator Pattern follows a (completely) different approach to deal with issues. Where the State Pattern allows for decentralized management, the Mediator Pattern centralizes that decision making into one place.

Lets look at our color-example.

class ColorMediator
{
   private var environment:Environment;
   private var colorHandlerList:Dictionary<string,ColorHandler>=new Dictionary<string,ColorHandler>(); 
  // As we want to keep full control we keep the state internal
   public var currentColorHandler;
   public ColorMediator(environment: Environment) 
   {
       // Set environment
       this.environment=environment;
   }
   public injectColorHandler(fromColorHandler, toColorHandler) 
   {
     // We simplify management in this example by using a lookup table
      colorHandlerList.add(fromColorHandler.color, toColorHandler);
   }
   public method init()
   {
      // We activate the current handler
      this.currentColorHandler.go();
   } 
   public method setNextColor()
   {
      // One of the options to manage/mediate is to supply the information
      //  we need to decide which next color/handler to use
      // As we use a lookup table, we can avoid the dreaded Switch statements
      // you normally see in an example like this.
      var nextColorHandler:ColorHandler= colorHandlerList[this.currentColorHandler];
      if(nextColorHandler==null)
      {
         // OOPS! you configured the color sequence wrongly
         // As this is a Mediator, you can choose to solve this by offering an alternative path
          nextColorHandler = colorHandlerList.Values[0]; // get first value
       }
       // Active the next color handler and set the color on the environment
       nextColorHandler.go(this.environment);
   }
}

There is a lot of things to say about this approach. I limit myself to 9 points:

  1. This is a very simple Mediator – When your application grows, more and more business rules will pop up in your application. Those situations are out of scope of this post, but will be clarified in a later post.
  2. We map the next Color Handler in a Lookup Table – Against the previous Color. This is a neat trick Dictionaries allow you to do. We could also have used the Color Handler itself, but that felt less clean.
  3. Do not fixate on this solution – There are many ways to write a (simple) Mediator and this is only one of them.
  4. I assume the colorHandlerList has values – Which is very naive. The code should first check if there are any values in the color Handler List.
  5. The color manager only knows one direction – Which is forward. It might also be allowed to branch on specific conditions, or move backwards.
  6. If you want to be able to navigate colors in more directions, discard this approach – Or the mapping issues it creates will drive you nuts
  7. I inject the Environment into the Color Handler – Which reduces depencencies and makes the Color Handler a neutral object, only required to deal with its internal stuff.
  8. I no longer keep State in the Environment – Which is smart if your application becomes more and more complex. The Environment can become a dumb object with just one simple task. All (state) management is done in your mediator.
  9. We now need 3 classes instead of 2 – As the management part is now externalized and centralized in the Color Mediator Class

Our ColorHandler might look like this:

class ColorHandler
{
     private var color;
     public ColorHandler(color) 
     {
             // Store the color
             this.color = color;
     }
     public go(environment:Environment)
     {
             // Set the stored color on the given environment
             environment.backgroundColor=this.color;
             // Done
      }
}

Our main Class now might look something like this:

class Environment
{
   private var redHandler, greeHandler, blueHandler : ColorHandler;
   // Removed: private var currentState :ColorState;
 // Added  private var mediator: ColorMediator=new ColorMediator()
   public Environment()
   { 
      // Define the handlers
       blueHandler=new ColorHandler (#0000ff);
       redHandler=new ColorHandler(#ff0000); 
       greenHandler=new ColorHandler(#00ff00);
       // Add them to the Mediator, with "from" color and "to" color handler 
      mediator.injectColorHandler(redHandler,greenHandler);
       mediator.injectColorHandler(greenHandler, blueHandler);
       mediator.injectColorHandler(blueHandler, redHandler);

       // Set current state - this will set the color and inject the next state
       mediator.currentColorHandler =blueHandler

       // Activate the state to start the process
       mediator.init();
       // Set the button click handler
       colorChangeButton.onClick=onButtonClick; 
   }
   public method onButtonClick(evt:Event)
   {
        // As our mediator deals with all state specific administration,
       // we just call the method and trust our mediator to deal with the rest
        mediator.setNextColor();
       // Done
    }
}

Both Mediator and State externalize and encapsulate the logic to deal with context-specific (user) actions.

Three main benefits of the Mediator – in this example

This delivers you a series of benefits of which I name 3:

  1. Cleaner code – Instead of having a Environment Class with a load of code dealing with each specific state (the colors) we now have State Handlers and/ore Color Handlers and a Mediator to deal with this
  2. Agilitiy – As you externalized and encapsulated the decision making logic, your code becomes more agile. Meaning that if you want to try a different approach to your decision making process, you simply create a new or different set of State Handlers and/or Mediators which you can plug in and remove to your own liking
  3. More stable code base and less errors due to mess – As each Handler (should have) a clear purpose, changes are either an improvement on existing code, or done in a new class that (possibly) extends your current code. This allows for code changes and changes of mind to be isolated and reversible. – If you do not like solution “Y” in Class “Z”, you simply use solution “A” in Class “B” instead. As your code stays clean, the risk of messing up code that worked well previously is reduced as well.

Where I do things differently – using Maps to cancel out Switch statements

You might have noticed my use of a Dictionary in the Mediator and injection of Handlers using mediator.inectHandler. If you take the code example from “Design Patterns for Dummies”, “Design Patterns” or “C# Design Patterns” and many other resources you will find in examples like the one above (almost by default) a Switch statement like this:

// The default Switch Statement you find in a lot of code (examples)
Switch ( myValue)
{
   case  SOME_CONSTANT1:
      // Do something if this condition is met
      break;
   case SOME_CONSTANT2:
      // Do something else  break; 
   // and so on...
   default:
      // capture when no condition is met
}

This seems quite harmless and – as many people and many authors of books on coding use it and – completely OK.

Think twice. Think clean code. Think of what is happening here: using hardcoded references to map events or objects to return specific values or perform specific actions.

See the specific post on “Using Maps to Cancel out Switch Statements” for more information on mapping objects, classes and methods to values and constants.

Using State Machines

One weakness of the implementation of the State Pattern as shown above is – as mentioned in the brief on the Mediator Pattern – is that the State of (a part of) the application is set in the Environment the State Handler controls. The more State variables you will have to remember for a specific Environment (think of tabs hiding and showing specific content, buttons and (text) fields being shown and hidden in specific states, and so on) the more your Environment becomes cluttered with variables you might want to manage somewhere else. (Think MVC here, where the View is just presenting data and has no awareness of any State it might be in)

Another weakness is that the State Handlers themselves hold references to the Environment.

When you start refactoring your project, this entanglement of objects might be something you want to clean up and/or get rid of. For this purpose you can use a State Machine.

The State Machine lives between the Environment and your State Handlers. It is the proxy between your Controller (in an MVC situation) your State Handlers and your View. Any reference to any object is managed by the State Machine and when any State Handler wants to switch states and show or hide elements (for instance) it invokes a method on the State Machine, which will do the actual work on the actual objects.

This decoupling makes your State Handlers more versitile and more re-usable.

Think for instance of generic Application Wide State Handlers which take care of  showing and hiding data and specific visual elements when a User is logged in or just visiting as a Guest.

Concluding

The State and Mediator Pattern are most valuable in situations where you have:

  1. Responses to actions are based on the state of (a part of) the application – Where the state of (a part of) an application can be: “user is logged in/out”, “user is looking at tab 1 of the application”, “user is filling in part one of a form”, “user pressed on submit”, “system is unable to connect to the database” and so on. And where the response might be: hiding and showing elements the user does not- and does need at that stage of the application, blocking specific actions until the state changes to one where those actions are allowed, removing or moving or adding elements based on drag and drop or click actions (think visual editors).
  2. Bloated classes due to situation specific responses – Where specific actions based on If/then/else becomes start to bloat your classes towards 500 lines of code and more.

State and Mediator differ mostly in the way they manage State and State Specific Actions:

  1. State: Distributed management – Of State and State Specific actions. The State Pattern puts the responsibility of managing the (next) State into the hands of each individual State Handler.
  2. Mediator: Centralized management – Of State and State specific actions. Instead the Mediator manages the state of an application centrally and (usually) delegates the real implementation of the state related actions to separate objects, handlers or classes.
Advertisements