SDSU CS 635 Advanced Object-Oriented Design & Programming
Spring Semester, 2004
State & Chain of Responsibility
Previous    Lecture Notes Index    Next    
© 2004, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 23-Feb-04

References

Design Patterns: Elements of Resuable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides, Addison Wesley, 1995, pp. 305-314, 223-232

The Design Patterns Smalltalk Companion, Alpert, Brown, Woolf, Addision-Wesley, 1998, pp. 327-338, 225-245.

Doc 10, State & Chain of Responsibility Slide # 2

State

Example - SPOP

Simple Post Office Protocol

SPOP is used to download e-mail from a server

SPOP supports the following command:


USER & PASS Commands


USER with a username must come first
PASS with a password or QUIT must come after USER

If the username and password are valid, then the user can use other commands

LIST Command

Arguments: a message-number (optional)

If it contains an optional message number then returns the size of that message

Otherwise return size of all mail messages in the mailbox

Doc 10, State & Chain of Responsibility Slide # 3
RETR Command

Arguments: a message-number

Returns: the mail message indicated by the number


QUIT Command

Arguments: none

Updates mail box to reflect transactions taken during the transaction state, then logs user out

If session ends by any method except the QUIT command, the updates are not done



Doc 10, State & Chain of Responsibility Slide # 4
The Switch Statement

class SPop
   {
   static final int HAVE_USER_NAME = 2;
   static final int START = 3;
   static final int AUTHORIZED = 4;
   private int state = START;
   
   String userName;
   String password;
   
   public void user( String userName ) {
      switch (state) {
         case START:  {
            this.userName = userName;
            state = HAVE_USER_NAME;
            break;
         } 
         case HAVE_USER_NAME:  
         case AUTHORIZED:{
            endLastSessionWithoutUpdate();
            goToStartState()
            }
         }
      }

Doc 10, State & Chain of Responsibility Slide # 5
Implementation with Switch Statement Cont.

   public void pass( String password )
      {
      switch (state)
         {
         case START:  {
            giveWarningOfIllegalCommand();
            }
         case HAVE_USER_NAME: {
            this.password = password;
            if ( validateUser() )
               state = AUTHORIZED;
            else {
               sendErrorMessageOrWhatEver();
               userName = null;
               password = null;
               state = START;
            }
         case AUTHORIZED: {
            endLastSessionWithoutUpdate();
            goToStartState()
         }
         }
      }
   etc.   
   }


Doc 10, State & Chain of Responsibility Slide # 6
Using Polymorphism Implementation


class SPop {
   private SPopState state = new Start();
      
   public void user( String userName ) {
      state = state.user( userName );
   }
   
   public void pass( String password ) {
      state = state.pass( password );
   }
   
   public void list( int messageNumber ) {
      state = state.list( massageNumber );
   }
   
   etc.


Doc 10, State & Chain of Responsibility Slide # 7
SPopStates
Defines default behavior

abstract class SPopState {
   public SPopState user( String userName ) {
      return goToStartState(); 
   }
   
   public SPopState pass( String password ) {
      return goToStartState(); 
   }
   
   public SPopState list( int massageNumber ) {
      return goToStartState(); 
   }
   
   public SPopState retr( int massageNumber ) {
      return goToStartState(); 
   }
   
   public SPopState quit(  ) {
      return goToStartState(); 
   }
   protected SPopState goToStartState()  {
      endLastSessionWithoutUpdate();
      return new StartState();
   }
}


Doc 10, State & Chain of Responsibility Slide # 8
SpopStates - Continued

class Start extends SPopState {
   public SPopState user( String userName ) {
      return new HaveUserName( userName );
   }
}
   
class HaveUserName extends SPopState {
   String userName;
   
   public HaveUserName( String userName ) {
      this.userName = userName;
   }
      
   public SPopState pass( String password ) {
      if ( validateUser( userName, password )
         return new Authorized( userName );
      else
         return goToStartState();
   }
}


Doc 10, State & Chain of Responsibility Slide # 9
State

Intent

Allow an object to alter its behavior when its internal state changes. The object will appear to change it class.



Applicability


Use the State pattern in either of the following cases:




Doc 10, State & Chain of Responsibility Slide # 10

Issues

How much State in the State


In Example:


All the state & all real behavior is in SPopState & subclasses

This is an extreme example

In general the Context will have data & methods


That is only some aspects of the Context will alter its behavior


Doc 10, State & Chain of Responsibility Slide # 11
Issue

Who defines the state transitions?

The Context



class SPop
   {
   private SPopState state = new Start();
      
   public void user( String userName )
      {
      state.user( userName );
      state = new HaveUserName( userName );
      }
   public void pass( String password )
      {
      if ( state.pass( password ) )
         state = new Authorized( );
      else
         state = new Start();
      }


Doc 10, State & Chain of Responsibility Slide # 12
Who defines the state transitions?

The State


class SPop
   {
   private SPopState state = new Start();
      
   public void user( String userName )
      {
      state = state.user( userName );
      }
   
   public void pass( String password )
      {
      state = state.pass( password );
      }
   
   public void list( int messageNumber )
      {
      state = state.list( massageNumber );
      }


Doc 10, State & Chain of Responsibility Slide # 13
Issue

Sharing State Objects


Multiple contexts (SPops) can use the same state object if the state object has no instance variables

A state object can have no instance variables if:




Doc 10, State & Chain of Responsibility Slide # 14
Storing Instance Variables Elsewhere

Variant 1

SPop stores them and passes them to states

class SPop
   {
   private SPopState state = new Start();
   
   String userName;
   String password;
      
   public void user( String newName )
      {
      this.userName = newName;
      state.user( newName );
      }
   
   public void pass( String password )
      {
      state.pass( userName , password );
      }


Doc 10, State & Chain of Responsibility Slide # 15
Storing Instance Variables Elsewhere

Variant 2

SPop stores them and states get data from SPop

class SPop {
   private SPopState state = new Start();
   
   String userName;
   String password;
      
   public String userName() { return userName; }
   
   public String password() { return password; }
   
   public void user( String newName ) {
      this.userName = newName ;
      state.user( this );
   }
   etc.

class HaveUserName extends SPopState {
   public SPopState pass( SPop mailServer ) {
      String useName = mailServer.userName();
      etc.
   }
}

Doc 10, State & Chain of Responsibility Slide # 16
Issue

Creating and Destroying State Objects


Options:




Doc 10, State & Chain of Responsibility Slide # 17
Issue

Changing the Context Class for Real


Some languages allow an object to change its class


   | context |
   context := Start new.
   context changeClassTo: HaveUserName.
   context changeClassTo: Authorized.
So why not forget State pattern and use:



In VisualWorks Smalltalk

In CLOS the State pattern may not be needed

Doc 10, State & Chain of Responsibility Slide # 18

Consequences






Doc 10, State & Chain of Responsibility Slide # 19

State Verses Strategy


How to tell the difference

Rate of Change
Strategy

State

Exposure of Change

Strategy

State



Doc 10, State & Chain of Responsibility Slide # 20

Chain of Responsibility

Intent


Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

Class Structure




Sample Object Structure




Doc 10, State & Chain of Responsibility Slide # 21

Participants

Handler


ConcreteHandler




Consequences





Doc 10, State & Chain of Responsibility Slide # 22

Motivation


Context Help System



Doc 10, State & Chain of Responsibility Slide # 23

When to Use


When more than on object may handle a request, and the handler isn't known a priori

When you want to issue a request to one of several objects without specifying the receiver explicitly

When the set of objects that can handle a request should be specified dynamically


Doc 10, State & Chain of Responsibility Slide # 24
How does this differ from Decorator?








Doc 10, State & Chain of Responsibility Slide # 25
Chain of Command

Like the military



Doc 10, State & Chain of Responsibility Slide # 26

Implementation Issues

The successor chain


The concrete handlers may already have pointers to their successors, so just use them


Give each handler a link to its successor


Doc 10, State & Chain of Responsibility Slide # 27
Representing Requests


abstract class HardCodedHandler
   {
   private HardCodedHandler successor;
      
   public HardCodedHandler( HardCodedHandler aSuccessor)
      { successor = aSuccessor; }
      
   public void handleOpen()
      { successor.handleOpen(); }
      
   public void handleClose()
      { successor.handleClose(); }
      
   public void handleNew( String fileName)
      { successor.handleClose( fileName ); }
   }


Doc 10, State & Chain of Responsibility Slide # 28
Representing Requests


abstract class SingleHandler {
   private SingleHandler successor;
      
   public SingleHandler( SingleHandler aSuccessor) {
      successor = aSuccessor;
   }
      
   public void handle( String request) {
      successor.handle( request );
   }
}
      
class ConcreteOpenHandler extends SingleHandler {
   public void handle( String request) {
      switch ( request ) {
         case "Open" : do the right thing;
         case "Close" : more right things;
         case "New" : even more right things;
         default: successor.handle( request );
      }
   }
}

Doc 10, State & Chain of Responsibility Slide # 29
Representing Requests

abstract class SingleHandler {
   private SingleHandler successor;
      
   public SingleHandler( SingleHandler aSuccessor)
      {successor = aSuccessor; }
      
   public void handle( Request data)
      { successor.handle( data ); }
}
      
class ConcreteOpenHandler extends SingleHandler {
   public void handle( Open data)
      { // handle the open here }
}
   
class Request {
   private int size;
   private String name;
   public Request( int mySize, String myName)
      { size = mySize;  name = myName; }
   
   public int size() { return size; }
   public String name() { return name;}
}
   
class Open extends Request
   {// add Open specific stuff here}
   
class Close extends Request
   { // add Close specific stuff here}


Doc 10, State & Chain of Responsibility Slide # 30

Object-Oriented Recursion

Recursive Delegation

A method polymorphically sends its message to a different receiver

Eventually a method is called that performs the task

The recursion then unwinds back to the original message send


Doc 10, State & Chain of Responsibility Slide # 31
Example

class BinarySearchTree {
   Node root
   
   boolean containsKey( Object key ) {
      return root.containsKey(key);
   }
   
   String toString() {
      return "Tree( " + root.toString() + ")";
   }
   blah
}

Doc 10, State & Chain of Responsibility Slide # 32
Example Continued
class BinaryNode implements Node {
   Node left;
   Node right;
   Object key;
   Object value;
   
   boolean containsKey( Object key ) {
      if this.key == key
         return true;
      if this.key < key
         return right.containsKey(key);
      if this.key > key
         return left.containsKey( key);
   }
   
   String toString() {
      return "( " + left.toString() + key + right.toString() + ")";
   }
   blah
}
class NullNode implements Node {
   
   boolean containsKey( Object key ) {
      return false;
   }
   
   String toString() {
      return " ";
   }
   blah
}


Copyright ©, All rights reserved.
2004 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.

Previous    visitors since 23-Feb-04    Next