CS 635: Advanced Object-Oriented Design & Programming |
---|
Flyweight | slide # 2 |
...Motivation | slide # 2 |
...Structure | slide # 4 |
......Applicability | slide # 5 |
...Implementation | slide # 8 |
Memento | slide # 9 |
...Motivation | slide # 9 |
...Structure | slide # 10 |
...Applicability | slide # 10 |
...An Example | slide # 11 |
...Consequences/ Implementation | slide # 14 |
Iterators, Mementos | slide # 15 |
...Momentos & Internal Iterators | slide # 16 |
...Trees & Iteration | slide # 17 |
...Composite Structures & Iteration | slide # 20 |
Using objects allows the program to treat characters like any other part of the document - as an object
A character, like "G", may require a fair amount of information:
Most of this information is the same for all instances of the same letter
The one object can store the intrinsic state, but someone else
needs to store the extrinsic state for each context of the object
In concurrent server we may have many (100's ) different users connected at the same time.
State creation can become expensive, so create each state once
Each time use the state pass the user name to the state
class HaveUserName extends SPopState { private HaveUserName() {}; private static HaveUserName instance; public getInstance( ) { if ( instance == null ) instance = new HaveUserName(); return instance; } public SPopState pass( String userName, String password ) { if ( validateUser( userName, password ) return Authorized.getInstance(); else return Start.getInstance(); } }
Must remove the extrinsic state from the object
Store the extrinsic state elsewhere taking up less space
Each time you use the flyweight you must give it the proper extrinsic state
Need factory to create flyweights, cannot create directly
How do we know when we are done with a flyweight?
But we have Command and Clone, why do we need another pattern for undo?
Clone can be more expensive than we need
package Examples;
class Memento { private Hashtable savedState = new Hashtable(); protected Memento() {}; //Give some protection protected void setState( String stateName, Object stateValue ) { savedState.put( stateName, stateValue ); } protected Object getState( String stateName) { return savedState.get( stateName); } protected Object getState(String stateName, Object defaultValue ) { if ( savedState.containsKey( stateName ) ) return savedState.get( stateName); else return defaultValue; } }
package Examples; class ComplexObject { private String name; private int someData; private Vector objectAsState = new Vector(); public Memento createMemento() { Memento currentState = new Memento(); currentState.setState( "name", name ); currentState.setState( "someData", new Integer(someData) ); currentState.setState( "objectAsState", objectAsState.clone() ); return currentState; } public void restoreState( Memento oldState) { name = (String) oldState.getState( "name", name ); objectAsState = (Vector) oldState.getState( "objectAsState" ); Integer data = (Integer) oldState.getState( "someData"); someData = data.intValue(); }
// Show a way to do incremental saves public Memento setName( String aName ) { Memento deltaState = saveAState( "name", name); name = aName; return deltaState; } public void setSomeData( int value ) { someData = value; } private Memento saveAState( String stateName, Object stateValue ) { Memento currentState = new Memento(); currentState.setState( stateName, stateValue ); return currentState; } }
class Vector { protected Object elementData[]; protected int elementCount; protected int currentPosition; public boolean hasMoreElements() { return currentPosition < elementCount; } public Object nextElement() { if (currentPosition < elementCount) return elementData[currentPosition++]; throw new NoSuchElementException("VectorIterator"); } // all vector methods not shown }
class IteratorState { int currentPosition = 0; protected IteratorState() {} protected int getPosition() { return currentPosition; } protected void advancePosition() { currentPosition++; } // Other methods? } class Vector { protected Object elementData[]; protected int elementCount; public IteratorState newIteration() { return new IteratorState(); } public boolean hasMoreElements(IteratorState aState) { return aState.getPosition() < elementCount; } public Object nextElement( IteratorState aState ) { if (hasMoreElements( aState ) ) { int currentPosition = aState.getPosition(); aState.advancePosition(); return elementData[currentPosition]; } throw new NoSuchElementException("VectorIterator"); } // all vector methods not shown }
In a given location there may be three directions to go
An iteration may go through the same node three times, may need to store the which visit this is to a particular node
If the tree does not support parent pointers one needs to store the stack of node from the current node to the root
public class TraversalStorage { BinaryNode storedNode; int visitNumber = 1; public TraversalStorage( BinaryNode aNode, int visitNumber ) { storedNode = aNode; this.visitNumber = visitNumber; } }
The following code does visit each node, but does nothing at each node
import java.util.Stack; public class TreeTraversal { Stack history = new Stack(); BinaryNode currentNode; int visitNumber; public TreeTraversal( BinaryNode startNode ) { currentNode = startNode; visitNumber = 1; } public void VisitAllNodes() { while ( ( ! history.empty() ) && ( visitNumber != 3)) { switch ( visitNumber ) { case 1: processFirstVisit( currentNode ); break; case 2: processSecondVisit( currentNode ); break; case 3: processThirdVisit( currentNode ); break; } } }
private void processFirstVisit( BinaryNode currentNode) { if ( currentNode.getLeftChild() != null ) { history.push( new TraversalStorage( currentNode, 2)); currentNode = (BinaryNode )currentNode.getLeftChild(); visitNumber = 1; } else processSecondVisit( currentNode ); } private void processSecondVisit( BinaryNode currentNode) { if ( currentNode.getRightChild() != null ) { history.push( new TraversalStorage( currentNode, 3)); currentNode = (BinaryNode) currentNode.getRightChild(); visitNumber = 1; } else processThirdVisit( currentNode ); } private void processThirdVisit( BinaryNode currentNode) { if ( ! history.empty() ){ TraversalStorage nextNode = (TraversalStorage) history.pop(); currentNode = nextNode.storedNode; visitNumber = nextNode.visitNumber; } } }
If the tree is contains BinaryNodes, UnaryNodes, and ExternalNodes, the above code will not work
We could add a selection for the type of the node to get code that looks something like:
private void processFirstVisit( Node currentNode) { if ( Node.type() == BINARY ) processFirstBinaryVisit( (BinaryNode) currentNode); else if ( Node.type() == UNARY ) processFirstUnaryVisit( (UnaryNode) currentNode); else if ( Node.type() == EXTERNAL ) processFirstExternalVisit( (ExternalNode) currentNode); }
If we were just doing binary trees this code is overly complex
The design patterns are not "needed" for small programming assignments done at school
In BinaryNode:
public void accept( Visitor aVisitor) { aVisitor.visitBinaryNode( this ); }
public void accept( Visitor aVisitor){ aVisitor.visitExternalNode( this ); }
public void accept( Visitor aVisitor){ aVisitor.visitUnaryNode( this ); }
import java.util.Stack; public class TraversalVisitor implements Visitor { Node currentNode; int visitNumber; int traversalType; Stack traversalPath = new Stack(); public TraversalVisitor( Node startNode ) { currentNode = startNode; visitNumber = 1; } public boolean hasMoreElements() { if ( ( traversalPath.empty() ) && ( visitNumber >= 3 )) return false; else return true; } public Node nextElement() { currentNode.accept( this ); return currentNode; }
public void visitBinaryNode( BinaryNode aNode) { switch ( visitNumber ) { case 1: pushNode( aNode, 2); currentNode = aNode.getLeftChild(); visitNumber = 1; break; case 2: pushNode( aNode, 3); currentNode = aNode.getRightChild(); visitNumber = 1; break; case 3: popNode(); } }
public void visitUnaryNode( UnaryNode aNode) { switch ( visitNumber ) { case 1: visitNumber = 2; break; case 2: pushNode( aNode, 3); currentNode = aNode.getChild(); visitNumber = 1; break; case 3: popNode(); } }
public void visitExternalNode( ExternalNode aNode) { switch ( visitNumber ) { case 1: visitNumber = 2; break; case 2: visitNumber = 3; break; case 3: popNode(); } } private void pushNode( Node aNode, int visitNumber ) { traversalPath.push( new TraversalStorage( aNode, visitNumber) ); } private void popNode( ) { if ( ! traversalPath.empty() ) { TraversalStorage nextNode = (TraversalStorage) traversalPath.pop(); currentNode = nextNode.getNode(); visitNumber = nextNode.getVisitNumber(); } else visitNumber = 3; } }