CS 635 Advanced Object-Oriented Design & Programming Spring Semester, 2002 Composite & Visitor |
||
---|---|---|
© 2002, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 12-Feb-02 |
Composite
Example - Motivation
GUI Windows and GUI elements
How does the window hold and deal with the different items it has to manage?
Widgets are different that WidgetContainers
Bad News Implementation
class Window { Buttons[] myButtons; Menus[] myMenus; TextAreas[] myTextAreas; WidgetContainer[] myContainers; public void update() { if ( myButtons != null ) for ( int k = 0; k < myButtons.length(); k++ ) myButtons[k].refresh(); if ( myMenus != null ) for ( int k = 0; k < myMenus.length(); k++ ) myMenus[k].display(); if ( myTextAreas != null ) for ( int k = 0; k < myButtons.length(); k++ ) myTextAreas[k].refresh(); if ( myContainers != null ) for ( int k = 0; k < myContainers.length(); k++ ) myContainers[k].updateElements(); etc. } public void fooOperation() { if ( blah ) etc. } }
A Better Idea - Program to an interface
class Window { GUIWidgets[] myWidgets; WidgetContainer[] myContainers; public void update() { if ( myWidgets != null ) for ( int k = 0; k < myWidgets.length(); k++ ) myWidgets[k].update(); if ( myContainers != null ) for ( int k = 0; k < myContainers.length(); k++ ) myContainers[k].updateElements(); etc. } }
The Composite Pattern
Component implements default behavior for widgets when possible
Button, Menu, etc overrides Component methods when needed
WidgetContainer will have to overrides all widgetOperations
class WidgetContainer { Component[] myComponents; public void update() { if ( myComponents != null ) for ( int k = 0; k < myComponents.length(); k++ ) myComponents[k].update(); } }
Issue: WidgetContainer Operations
WidgetContainer operations tend to relate to adding, deleting and managing widgets
Should the WidgetContainer operations be declared in Component?
Pro - Transparency
Declaring them in the Component gives all subclasses the same interface
All subclasses can be treated alike. (?)
Con - Safety
Declaring them in WidgetContainer is safer
Adding or removing widgets to non-WidgetContainers is an error
What should be the proper response to adding a TextArea to a button? Throw an exception?
One out is to check the type of the object before using a WidgetContainer operation
Explicit Parent References
Aid in traversing the structure
class WidgetContainer { Component[] myComponents; public void update() { if ( myComponents != null ) for ( int k = 0; k < myComponents.length(); k++ ) myComponents[k].update(); } public add( Component aComponent ) { myComponents.append( aComponent ); aComponent.setParent( this ); } } class Button extends Component { private Component parent; public void setParent( Component myParent) { parent = myParent; } etc. }
More Issues
Should Component implement a list of Components?
Applicability
Use Composite pattern when you want
Java Use of Composite - AWT Widgets
Specialized Java Containers
Visitor
Example - Trees
What about preorder visit, postorder visit?
What about an HTML print?
What about printing a 2D representation?
What if this was an expression tree - evaluation?
What if this was a binary search tree - adding, deleting
What about balancing the binary search tree -
Solution 1
Build all the operations into the tree structure
This works in many situations
If you need to add a lot of operations the classes can become cluttered
class BinaryTree { public String htmlPrint() { blah} public String 2DPrint() { blah} public String preorderTraversal() { blah} public String postorderTraversal() { blah} public String avlAdd() { blah} public String RedBlackAdd() { blah} etc. }
Solution 2
Use different classes or subclasses for the different types of trees
Does not work in classes we want to be able to mix all combinations
class BinaryTree { blah } class AVLTree extends BinaryTree { blah } class RedBlackTree extends BinaryTree { blah } class PreorderTree extends BinayrTree { blah } etc.
Solution 3
class BinaryTreeNode extends Node { public void accept(Visitor aVisitor) { aVisitor.visitBinaryTreeNode( this ); } etc. }
class BinaryTreeLeaf extends Node { public void accept(Visitor aVisitor) { aVisitor.visitBinaryTreeLeaf( this ); } etc. }
abstract class Visitor { abstract void visitBinaryTreeNode( BinaryTreeNode ); abstract void visitBinaryTreeLeaf( BinaryTreeLeaf ); } class HTMLPrintVisitor extends Visitor { public void visitBinaryTreeNode( BinaryTreeNode ) { HTML print code here } etc. }
Double Dispatch
Note that a visit to one node requires two method calls
Node example = new BinaryTreeLeaf(); Visitor traveler = new HTMLPrintVisitor(); example.accept( traveler );
example.accept() calls aVisitor.visitBinaryTreeNode(this);
The first method selects the correct method in the Visitor class
The second method selects the correct Visitor class
Structure
This is more complex than solutions 1 & 2
The structure, an iterator, or the visitor can do the traversal
When to Use Visitor
Consequences
Visitor>>visit: anElement self perform: ('visit' , anElement class name , ':') asSymbol with: anElement