SDSU CS 635 Advanced Object-Oriented Design & Programming
Spring Semester, 2002
State, Proxy, Adaptor
Previous    Lecture Notes Index    Next    
© 2002, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 28-Feb-02

References

Design Patterns: Elements of Resuable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides, Addison Wesley, 1995, pp . 139-150, 207-217, 305-314

Pattern-Oriented Software: A System of Patterns, Buschman, Meunier, Rohnert, Sommerlad, Stal, 1996, pp. 263-275

Advanced C++: Programming Styles and Idioms, James Coplien, 1992, pp. 58-72

The Design Patterns Smalltalk Companion, Alpert, Brown, Woolf, Addision-Wesley, 1998, pp. 105-120, 213-222, 327-338

Smalltalk Scaffolding Patterns, Doble & Auer in Pattern Languages of Program Design 4, Addison Wesley, Eds. Harrison, Foote, Rohnert, 2000, pp. 199-219


Doc 9, State, Proxy, Adaptor Slide # 2

Design Principle 2



Favor object composition over class inheritance

Composition



Inheritance

class A {
   Foo x
   public int complexOperation() {   blah }
}
class B extends A {
   public void bar() { blah}
}
Composition

class B {
   A myA;
   public int complexOperation() {
      return myA.complexOperation()
   }
   public void bar() { blah}
}



Doc 9, State, Proxy, Adaptor Slide # 3

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 9, State, Proxy, Adaptor Slide # 4
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 9, State, Proxy, Adaptor Slide # 5
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 9, State, Proxy, Adaptor Slide # 6
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 9, State, Proxy, Adaptor Slide # 7
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 9, State, Proxy, Adaptor Slide # 8
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 9, State, Proxy, Adaptor Slide # 9
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 9, State, Proxy, Adaptor Slide # 10
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 9, State, Proxy, Adaptor Slide # 11

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 9, State, Proxy, Adaptor Slide # 12
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 9, State, Proxy, Adaptor Slide # 13
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 9, State, Proxy, Adaptor Slide # 14
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 9, State, Proxy, Adaptor Slide # 15
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 9, State, Proxy, Adaptor Slide # 16
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 9, State, Proxy, Adaptor Slide # 17
Issue

Creating and Destroying State Objects


Options:




Doc 9, State, Proxy, Adaptor Slide # 18
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 9, State, Proxy, Adaptor Slide # 19

Consequences






Doc 9, State, Proxy, Adaptor Slide # 20

State Verses Strategy


How to tell the difference

Rate of Change
Strategy
Context object usually contains one of several possible ConcreteStrategy objects

State
Context object often changes its ConcreteState object over its lifetime

Exposure of Change

Strategy
All ConcreteStrategies do the same thing, but differently
Clients do not see any difference in behavior in the Context

State
ConcreteState act differently
Clients see different behavior in the Context

Doc 9, State, Proxy, Adaptor Slide # 21

Proxy

proxy n. pl prox-ies The agency for a person who acts as a substitute for another person, authority to act for another

Structure




The Pattern

The proxy has the same interface as the original object

Use common interface (or abstract class) for both the proxy and original object

Proxy contains a reference to original object, so proxy can forward requests to the original object


Doc 9, State, Proxy, Adaptor Slide # 22

Dynamics




Runtime Objects



Doc 9, State, Proxy, Adaptor Slide # 23
Sample Proxy

public class Proxy 
   {
   Foo target;
   public float bar(int size )
      {
      preprocess here
      float answer =  target.bar( size);
      postProcess here
      return answer;
      }
   
   other methods as needed
   }

Preprocessing & post-processing depend on purpose of the proxy

Doc 9, State, Proxy, Adaptor Slide # 24

Reasons for Object Proxies

Remote Proxy
The actual object is on a remote machine (remote address space)

Hide real details of accessing the object

Used in CORBA, Java RMI


public class HelloClient  {
   public static void main(String args[])  {
      try  {
         String server = getHelloHostAddress( args);
         Hello proxy = (Hello) Naming.lookup( server );
         String message = proxy.sayHello();
         System.out.println( message );
      } 
      catch ( Exception error) 
         {  error.printStackTrace(); }
   }
}

Doc 9, State, Proxy, Adaptor Slide # 25
Reasons for Object Proxies Continued

Virtual Proxy



Protection Proxy

Cache Proxy (Server Proxy)

Firewall Proxy


Doc 9, State, Proxy, Adaptor Slide # 26
Synchronization Proxy

public class Table {   
   public Object elementAt( int row, int column ){ blah   }
   public void setElementAt(Object element, int row, int column )
      { blah}
}
   
public class RowLockTable {
   Table realTable;
   Integer[] locks;
   
   public RowLockTable( Table toLock) {
      realTable = toLock;
      locks = new String[ toLock.numberOfRows() ];
      for (int row = 0; row< toLock.numberOfRows(); row++ )
         locks[row] = new Integer(row);
      }
   
   public Object elementAt( int row, int column ) {
      synchronized ( locks[row] ) {
         return realTable.elementAt( row, column);
      }
   }
   public void setElementAt(Object element, int row, int column ){
      synchronized ( locks[row] )   {
         return realTable.setElementAt(element, row, column);
      }
   }
}

Doc 9, State, Proxy, Adaptor Slide # 27
Counting Proxy

Delete original object when there are no references to it

Prevent accidental deletion of real subject

Collect usage statistics

Sample use is making C++ pointer safe

Doc 9, State, Proxy, Adaptor Slide # 28

Smalltalk Proxy Tricks


When an object is sent a message

The object's class and the object's class’s superclasses are searched for the method

If the method is not found the object is sent the message:

doesNotUnderstand:

This method in Object raises an exception


Doc 9, State, Proxy, Adaptor Slide # 29
Prototyping of a Proxy

One can use doesNotUnderstand: to implement a pluggable proxy

Example

Object subclass: #Proxy
   instanceVariableNames: 'target '
   classVariableNames: ''
   poolDictionaries: ''
   category: 'Whitney-Examples'

Class Method

on: anObject
   ^super new target: anObject

Instance Methods

doesNotUnderstand: aMessage 
   ^target
      perform: aMessage selector 
      withArguments: aMessage arguments

target: anObject
   target := anObject


Doc 9, State, Proxy, Adaptor Slide # 30
Examples of Using the Proxy

| wrapper |
wrapper := Proxy on: Transcript.
wrapper open.
wrapper show: 'Hi mom'.

| wrapper |
wrapper := Proxy on: 3.
wrapper + 5.

| wrapper |
wrapper := Proxy on: 'Hi '.
wrapper , ' mom'.


Doc 9, State, Proxy, Adaptor Slide # 31
Why just for Prototyping

doesNotUnderstand:



Doc 9, State, Proxy, Adaptor Slide # 32

Adapter

Motivating Adapter

Java CGI & Servlets

Both Java CGI and servlets are used for server-side processing of certain HTML requests, like processing HTML forms

Servlets have greater functionality and are faster, but require special Web servers or servers with special extensions

To help write Java CGI programs there is class sdsu.net.CGI

It would be useful in moving code between servers to avoid having to rewrite the code


Doc 9, State, Proxy, Adaptor Slide # 33
One Problem

One issue is access to the CGI environment variables

There are about 20 common CGI environment variables

In servlets one has an HttpRequest class that has a getX() method for each CGI environment variable

sdsu.net.CGI class returns a hash table with one entry per CGI environment variable

Doc 9, State, Proxy, Adaptor Slide # 34
Solution

We can write a wrapper around HttpRequest to make it act like a hash table

The Wrapper or Adapter

class CGIAdapter extends Hashtable
   {
   Hashtable CGIvariables = new Hashtable( 20);
   
   public CGIAdapter( HttpRequest CGIEnvironment )
      {
      CGIvariables.put( "AUTH_TYPE" , 
                     CGIEnvironment.getAuthType());
                     
      CGIvariables.put( "REMOTE_USER" , 
                     CGIEnvironment.getRemoteUser());
      
      etc.
      }
   
   public Object get(Object key)
      {
      return CGIvariables.get( key );
      }
      
      etc. 
   }



Doc 9, State, Proxy, Adaptor Slide # 35
Going the other Direction

Adapting servlet code to normal CGI requires extracting the CGI environment variables out of the hash table and putting them into an object that implements the public interface of the HttpRequest class


class HTTPAdapter extends HttpRequest
   {
   Hashtable CGIvariables;
   
   public HTTPAdapter( Hashtable CGIEnvironment )
      {
      CGIvariables = CGIEnvironment;
      }
   
   public String getAuthType()
      {
      return (String) CGIvariables.get( "AUTH_TYPE" );
      }
      
   public String getRemoteUser()
      {
      return (String) CGIvariables.get( "REMOTE_USER" );
      }
      
      etc. 
   }


Doc 9, State, Proxy, Adaptor Slide # 36

Adapter


The adapter pattern converts the interface of a class into another interface.


Use the Adapter pattern when




Adapter has two forms:



Doc 9, State, Proxy, Adaptor Slide # 37
Class Adapter



Object Adapter





Doc 9, State, Proxy, Adaptor Slide # 38
Class Adapter Example

class OldSquarePeg {
   public:
      void squarePegOperation()
         { do something }
}
   
class RoundPeg {
   public:
      void virtual roundPegOperation = 0;
}
   
class PegAdapter: private OldSquarePeg, 
            public RoundPeg  {
   public:
      void virtual roundPegOperation()  {
         add some corners;
         squarePegOperation();
      }
}
   
void clientMethod() {
   RoundPeg* aPeg = new PegAdapter();
   aPeg->roundPegOperation();
}


Doc 9, State, Proxy, Adaptor Slide # 39
Object Adapter Example

class OldSquarePeg   {
   public:
      void squarePegOperation() { do something }
   }
   
class RoundPeg   {
   public:
      void virtual roundPegOperation = 0;
   }
   
class PegAdapter:  public RoundPeg   {
   private:
      OldSquarePeg* square;
      
   public:
      PegAdapter()  { square = new OldSquarePeg; }
         
      void virtual roundPegOperation()   {
         add some corners;
         square->squarePegOperation();
         }
   }


Doc 9, State, Proxy, Adaptor Slide # 40

Consequences


A Class adapter uses inheritance so




An object adapter uses object composition so



Other issues:





Doc 9, State, Proxy, Adaptor Slide # 41
How Much Adapting does the Adapter do?

The adapter may have to work very little or a great deal to adapt the Adaptee to the Target

The Adapter may just map one operation to another

class PegAdapter:  public RoundPeg   {
   private:
      OldSquarePeg* square;
      
   public:
      PegAdapter()   { square = new OldSquarePeg;}
         
      void roundPegOperation()   {
         square->squarePegOperation();
      }
}

The Adapter may have to work hard if the Target operation does not have a comparable operation in the Adaptee


Doc 9, State, Proxy, Adaptor Slide # 42
Pluggable Adapters

In the CGI example we adapted a class with getX() methods to a hash table interface

It is likely that we may adapt a class with getX() methods to a hashtable in the future

It would be nice to write one class to do all such adapting

This class would be given a list of keys to getX methods and an Adaptee object

HttpRequest CGIEnvironment = getHttpRequest();
PluggableHashAdapter sample = 
   new PluggableHashAdapter( CGIEnvironment );
   
sample.adapt( "AUTH_TYPE" , getAuthType );
sample.adapt( "REMOTE_USER" , getRemoteUser );
etc.
   
sample.get( “REMOTE_USER” );

Pluggable Adapters are used in interface components, where we know in advance that we will adapt the component to other interfaces

Pluggable Adapters are common in Smalltalk, were it is easier to map strings to method calls


Doc 9, State, Proxy, Adaptor Slide # 43
Using two-way Adapter

In the SquarePeg-RoundPeg example the SquarePeg is adapted to the RoundPeg

So a SquarePeg can be used where a RoundPeg is needed, but not the other way around.

A two-way adapter would also allow a RoundPeg be used in place of the SquarePeg

class OldSquarePeg {
   public:
      void virtual squarePegOperation() { blah }
}
   
class RoundPeg {
   public:
      void virtual roundPegOperation() { blah }
}
   
class PegAdapter: public OldSquarePeg, RoundPeg {
   public:
      void virtual roundPegOperation() {
         add some corners;
         squarePegOperation();
      }
      void virtual squarePegOperation() {
         add some corners;
         roundPegOperation();
      }
}


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

Previous    visitors since 28-Feb-02    Next