![]() |
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