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


References

Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides, Addison-Wesley, 1995, pp. 233-242, 175-185, 207-217

The Design Patterns Smalltalk Companion, Alpert, Brown, Woolf, Addision-Wesley, 1998, pp. 245-260, 161-178, 213-221

Pattern-Oriented Software Architecture: A System of Patterns, Buschman, Meunier, Rohnert, Sommerlad, Stal, 1996, pp. 277-290, Command Processor

Command Processor, Sommerlad in Pattern Languages of Program Design 2, Eds. Vlissides, Coplien, Kerth, Addison-Wesley, 1996, pp. 63-74

Advanced C++: Programming Styles and Idioms, James Coplien, Addison Wesley, 1992, pp 165-170, Functor Pattern


Doc 9, Command, Decorator & Proxy Slide # 2

Command


Encapsulates a request as an object

Structure




Example

Let
Invoker be a menu
Client be a word processing program
Receiver a document
Action be save


Doc 9, Command, Decorator & Proxy Slide # 3

When to Use the Command Pattern


Commands replace callback functions





A Transactions encapsulates a set of changes to data

Systems that use transaction often can use the command pattern



Doc 9, Command, Decorator & Proxy Slide # 4

Consequences


Command decouples the object that invokes the operation from the one that knows how to perform it

It is easy to add new commands, because you do not have to change existing classes

You can assemble commands into a composite object


Doc 9, Command, Decorator & Proxy Slide # 5
Example - Menu Callbacks

abstract class Command
   {
   abstract public void execute();
   }
   
class OpenCommand extends Command
   {
   private Application opener;
   
   public OpenCommand( Application theOpener )
      {
      opener = theOpener;
      }
   
   public void execute()
      {
      String documentName = AskUserSomeHow();
      
      if ( name != null )
         {
         Document toOpen = 
               new Document( documentName );
         opener.add( toOpen );
         opener.open();
         }
      }
   }


Doc 9, Command, Decorator & Proxy Slide # 6
Using Command

class Menu
   {
   private Hashtable menuActions = new Hashtable();
   
   public void addMenuItem( String displayString, 
                  Command itemAction )
      {
      menuActions.put( displayString, itemAction );
      }
   public void handleEvent( String itemSelected )
      {
      Command runMe;
      runMe = (Command) menuActions.get( itemSelected );
      runMe.execute();
      }
   // lots of stuff missing
   }



Doc 9, Command, Decorator & Proxy Slide # 7
MacroCommand

class MacroCommand extends Command
   {
   private Vector commands = new Vector();
   
   public void add( Command toAdd )
      {
      commands.addElement( toAdd );
      }
   
   public void remove( Command toRemove )
      {
      commands.removeElement( toAdd );
      }
   
   public void execute()
      {
      Enumeration commandList = commands.elements();
      
      while ( commandList.hasMoreElements() )
         {
         Command nextCommand;
         nextCommand = (Command)
                      commandList.nextElement();
         nextCommand.execute();
         }
      }
   }


Doc 9, Command, Decorator & Proxy Slide # 8
Prevayler

http://www.prevayler.org/wiki.jsp

Prevalence layer for Java

Database that


Doc 9, Command, Decorator & Proxy Slide # 9
Restaurant Example

import java.util.*;
import org.prevayler.implementation.AbstractPrevalentSystem;
public class Restaurant extends AbstractPrevalentSystem {
   private String name;
   ArrayList ratings = new ArrayList();
   
   public Restaurant(String newName) { name = newName;}
   
   public String name() {return name;}
   
   public void addRating( int newRating) {
      ratings.add( new Integer(newRating));
   }
   
   public float getRating() {
      if (ratings.size() == 0 ) 
         return -1;
      int total = 0;
      for (int k =0; k < ratings.size();k++)
         total = total + ((Integer)ratings.get(k)).intValue();
      return total/ ratings.size();
   }
}

Doc 9, Command, Decorator & Proxy Slide # 10
Command

import java.io.Serializable;
import org.prevayler.Command;
import org.prevayler.PrevalentSystem;
public class AddRatingCommand implements Command {
   private final int newRating;
   
   public AddRatingCommand(int rating) {
      newRating = rating;
   }
   
   public Serializable execute(PrevalentSystem system) {
      ((Restaurant)system).addRating(newRating);
      return null;
   }
}

Doc 9, Command, Decorator & Proxy Slide # 11
First Run

import java.util.*;
import org.prevayler.implementation.SnapshotPrevayler;
   
public class PrevaylerExample {
   
    public static void main (String args[]) throws Exception {
      SnapshotPrevayler samsDinerData = 
         new SnapshotPrevayler(new Restaurant("Sams Diner"), "food");
   
      System.out.println( “Start”);
      Restaurant samsDiner = (Restaurant) samsDinerData.system();
      System.out.println( samsDiner.getRating() );
      samsDinerData.executeCommand( new AddRatingCommand( 5));
      System.out.println( samsDiner.getRating() );
    }
}

Output
Recovering system state...
Start
-1.0
5.0

Doc 9, Command, Decorator & Proxy Slide # 12
Second Run

public class PrevaylerExample {
      
    public static void main (String args[]) throws Exception {
      SnapshotPrevayler samsDinerData = 
         new SnapshotPrevayler(new Restaurant("Sams Diner"), "food");
   
      System.out.println( “Start”);
      Restaurant samsDiner = (Restaurant) samsDinerData.system();
      System.out.println( samsDiner.getRating() );
      samsDinerData.executeCommand( new AddRatingCommand( 10));
      System.out.println( samsDiner.getRating() );
    }
}

Output
Recovering system state...
Reading food/000000000000000000001.commandLog...
Start
5.0
7.0


Doc 9, Command, Decorator & Proxy Slide # 13
Pluggable Commands

Using reflection it is possible to create one general Command

Don’t hard code the method called in the command

Pass the method to call an argument


Doc 9, Command, Decorator & Proxy Slide # 14
Java Example of Pluggable Command

import java.util.*;
import java.lang.reflect.*;

public class Command
   {
   private Object receiver;
   private Method command;
   private Object[] arguments;
   
   public Command(Object receiver, Method command, 
                           Object[] arguments )
      {
      this.receiver = receiver;
      this.command = command;
      this.arguments = arguments;
      }
   public void execute() throws InvocationTargetException, 
                                 IllegalAccessException
      {
      command.invoke( receiver, arguments );
      }
   }

Doc 9, Command, Decorator & Proxy Slide # 15
Using the Pluggable Command

One does have to be careful with the primitive types

public class Test {
   public static void main(String[] args) throws Exception 
      {
      Vector sample = new Vector();
      Class[] argumentTypes = { Object.class };
      Method add = 
         Vector.class.getMethod( "addElement", argumentTypes);
      Object[] arguments = { "cat" };
      
      Command test = new Command(sample, add, arguments );
      test.execute();
      System.out.println( sample.elementAt( 0));
      }
   }
Output
cat


Doc 9, Command, Decorator & Proxy Slide # 16
Pluggable Command Smalltalk Version

Object subclass: #PluggableCommand
   instanceVariableNames: 'receiver selector arguments '
   classVariableNames: ''
   poolDictionaries: ''
   category: 'Whitney-Examples'

Class Methods
receiver: anObject selector: aSymbol arguments: anArrayOrNil
   ^super new
      setReceiver: anObject 
      selector: aSymbol 
      arguments: anArrayOrNil

Instance Methods
setReceiver: anObject selector: aSymbol arguments: anArrayOrNil
   receiver := anObject.
   selector := aSymbol.
   arguments := anArrayOrNil isNil
               ifTrue:[#( )]
               ifFalse: [anArrayOrNil]

execute
   ^receiver 
      perform: selector 
      withArguments: arguments




Doc 9, Command, Decorator & Proxy Slide # 17
Using the Pluggable Command

| sample command |
sample := OrderedCollection new.
command := PluggableCommand
      receiver: sample
      selector: #add:
      arguments: #( 5 ).
command execute.
^sample at: 1


Doc 9, Command, Decorator & Proxy Slide # 18

Command Processor


Command Processor manages the command objects


The command processor:








Doc 9, Command, Decorator & Proxy Slide # 19

Structure



Dynamics




Doc 9, Command, Decorator & Proxy Slide # 20

Consequences

Benefits


Different user interface elements can generate the same kind of command object

Allows the user to configure commands performed by a user interface element


Adding new commands and providing for a macro language comes easy


Commands can be stored for later replay
Commands can be logged
Commands can be rolled back



Allows for the execution of commands in separate threads



Doc 9, Command, Decorator & Proxy Slide # 21
Liabilities



Try reducing the number of command classes by:
Grouping commands around abstractions
Unifying simple commands classes by passing the receiver object as a parameter


How do commands get additional parameters they need?


Doc 9, Command, Decorator & Proxy Slide # 22

Functor

Functions as Objects

A functor is a class with



Functors are functions that behave like objects

They serve the role of a function, but can be created, passed as parameters, and manipulated like objects

final class StudentNameComparator implements Comparator {
   
   public int compare( Object leftOp, Object rightOp ) {
      String leftName = ((Student) leftOp).name;
      String rightName = ((Student) rightOp).name;
      return leftName.compareTo( rightName );
   }
}



Doc 9, Command, Decorator & Proxy Slide # 23
How Does a Functor Compare to Function Pointers?


How does A Functor compare with Command?



When to use the Functor

Coplien states:

Use functors when you would be tempted to use function pointers

Functors are commonly used for callbacks

Doc 9, Command, Decorator & Proxy Slide # 24

Decorator


Changing the Skin of an Object

Class Structure



Runtime Structure



Doc 9, Command, Decorator & Proxy Slide # 25

Motivation - Text Views


A text view has the following features:

side scroll bar
Bottom scroll bar
3D border
Flat border

This gives 12 different options:

TextView
TextViewWithNoBorder&SideScrollbar
TextViewWithNoBorder&BottomScrollbar
TextViewWithNoBorder&Bottom&SideScrollbar
TextViewWith3DBorder
TextViewWith3DBorder&SideScrollbar
TextViewWith3DBorder&BottomScrollbar
TextViewWith3DBorder&Bottom&SideScrollbar
TextViewWithFlatBorder
TextViewWithFlatBorder&SideScrollbar
TextViewWithFlatBorder&BottomScrollbar
TextViewWithFlatBorder&Bottom&SideScrollbar

How to implement?


Doc 9, Command, Decorator & Proxy Slide # 26
Solution 1 - Use Object Composition



class TextView {
   Border myBorder;
   ScrollBar verticalBar;
   ScrollBar horizontalBar;
   
   public  void draw() {
      myBorder.draw();
      verticalBar.draw();
      horizontalBar.draw();
      code to draw self
   }   
   etc.
}

But TextView knows about all the variations!
New type of variations require changing TextView
(and any other type of view we have)

Doc 9, Command, Decorator & Proxy Slide # 27
Solution 2 - Use Decorator

Object Composition Inside out

Change the skin of an object not it guts

TextView has no borders or scrollbars!
Add borders and scrollbars on top of a TextView


Runtime Structure



Doc 9, Command, Decorator & Proxy Slide # 28

Applicability


Use Decorator:




Commonly used in basic system frameworks

Windows, streams, fonts


Consequences


More flexible than static inheritance

Avoids feature laden classes high up in hierarchy

Lots of little objects

A decorator and its components are not identical

So checking object identification can cause problems

   if ( aComponent instanceof TextView ) blah


Doc 9, Command, Decorator & Proxy Slide # 29

Implementation Issues


Keep Decorators lightweight

Don't put data members in VisualComponent

Have Decorator forward all component operations

Three ways to forward messages



Doc 9, Command, Decorator & Proxy Slide # 30

Examples

Java Streams



import java.io.*;
import sdsu.io.*;
class  ReadingFileExample
   {
   public  static  void  main( String  args[]  ) throws Exception
      {
      FileInputStream inputFile;
      BufferedInputStream bufferedFile;
      ASCIIInputStream  cin;
         
      inputFile = new FileInputStream( "ReadingFileExample.java" );
      bufferedFile = new BufferedInputStream( inputFile );
      cin = new ASCIIInputStream( bufferedFile );
   
      System.out.println(  cin.readWord()  );
   
      for  ( int  k = 1 ;  k  <  4;  k++ )
         System.out.println(  cin.readLine()  );
      }
   }

Doc 9, Command, Decorator & Proxy Slide # 31
Insurance

Insurance policies have payment caps for claims

Sometimes the people with the same policy will have different caps

A decorator can be used to provide different caps on the same policy object


Similarly for deductibles & copayments


Doc 9, Command, Decorator & Proxy Slide # 32

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, Command, Decorator & Proxy Slide # 33

Dynamics




Runtime Objects



Doc 9, Command, Decorator & Proxy Slide # 34
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, Command, Decorator & Proxy Slide # 35

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, Command, Decorator & Proxy Slide # 36
Reasons for Object Proxies Continued

Virtual Proxy



Protection Proxy

Cache Proxy (Server Proxy)

Firewall Proxy


Doc 9, Command, Decorator & Proxy Slide # 37
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, Command, Decorator & Proxy Slide # 38
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, Command, Decorator & Proxy Slide # 39

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, Command, Decorator & Proxy Slide # 40
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, Command, Decorator & Proxy Slide # 41
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, Command, Decorator & Proxy Slide # 42
Why just for Prototyping

doesNotUnderstand:




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 19-Feb-04    Next