CS 635 Advanced Object-Oriented Design & Programming Spring Semester, 2004 State & Strategy |
||
---|---|---|
© 2004, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 17-Feb-04 |
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
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
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. }
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. }
Adapter
The adapter pattern converts the interface of a class into another interface.
Use the Adapter pattern when
Class Adapter
Object Adapter
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(); }
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(); } }
Consequences
A Class adapter uses inheritance so
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
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
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(); } }
Strategy
Intent
Define a family of algorithms, encapsulate each one, and make them interchangeable
Strategy lets the algorithm vary independently from clients that use it
Structure
Examples
Java Layout Managers for Windows
Java Comparators
Smalltalk sort blocks
Java Layout Managers
import java.awt.*; class FlowExample extends Frame { public FlowExample( int width, int height ) { setTitle( "Flow Example" ); setSize( width, height ); setLayout( new FlowLayout( FlowLayout.LEFT) ); for ( int label = 1; label < 10; label++ ) add( new Button( String.valueOf( label ) ) ); show(); } public static void main( String args[] ) { new FlowExample( 175, 100 ); new FlowExample( 175, 100 ); } }
Why Not use Inheritance?
But there are:
Java Comparators
import java.util. Comparator; import java.util.*; class Student { String name; public Student( String newName) { name = newName;} public String toString() { return name; } } 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 ); } public boolean equals( Object comparator ) { return comparator instanceof StudentNameComparator; } } public class Test { public static void main(String args[]) { Student[] cs596 = { new Student( "Li" ), new Student( "Swen" ), new Student( "Chan" ) }; //Sort the array Arrays.sort( cs596, new StudentNameComparator() ); } }
Smalltalk SortBlocks
| list | list := #( 1 6 2 3 9 5 ) asSortedCollection. Transcript print: list; cr. list sortBlock: [:x :y | x > y]. Transcript print: list; cr; flush.
Why Not use Inheritance
There are arbitrarily many ways to sort
So get arbitrarily many
Applicability
Use the Strategy pattern when
Consequences
switch ( flag ) { case A: doA(); break; case B: doB(); break; case C: doC(); break; }
strategy.do();
SortedList studentRecords = new SortedList(new ShellSort());
Implementation
SortedList<ShellSort> studentRecords;