Computer Store
store.prestwood.com
-Collapse +Expand
Java
Search Java Group:

Advanced
-Collapse +Expand Java To/From
To/FromCODEGuides
-Collapse +Expand Java Store
PRESTWOODSTORE

Prestwood eMagazine

July Edition
Subscribe now! It's Free!
Enter your email:

   ► KBProgrammingJavaStandard JavaOOP   Print This     
  From the January 2016 Issue of Prestwood eMag
 
Java OOP:
Implementing the Composite and Visitor Patterns in Java
 
Posted 18 years ago on 12/12/2002 and updated 1/16/2009
Take Away:

Implementing design patterns can be difficult and little sample code exists that walks you through the process. The purpose of this article is to walk through the implementation of a program that uses the Composite and Visitor design patterns. The sample project is written in the object-oriented language, Java, but it could just as easily have been written in C++.

KB100084

Introduction

Implementing design patterns can be difficult and little sample code exists that walks you through the process.  The purpose of this article is to walk through the implementation of a program that uses the Composite and Visitor design patterns.  The sample project is written in the object-oriented language, Java, but it could just as easily have been written in C++.

Before we proceed, let us get acquainted with the Composite and Visitor patterns.

Composite

Below is an excerpt from Design Patterns describing the Composite pattern:

Compose objects into tree structures to represent part-whole hierarchies.  Composite lets clients treat individual objects and compositions of objects uniformly. (Gamma 163)

Composite closely resembles the structure of objects in the real world.  For example, the human body is a composition--or a part-whole hierarchy--of parts.  It contains a head, arms, legs, an ass, and so on.  Each of these components can been seen themselves as compositions of other components.  Composite makes it easy dynamically create such a object compositions at runtime.  Consider the following code:

// Create a Composite

HumanBody body = new Body();

// Add leaf objects to the Composite.

body.AddComponent( new Arm() );

body.AddComponent( new Arm() );

body.AddComponent( new Leg() );

body.AddComponent( new Leg() );

// Build a new child Composite object.

Head head = new Head();

head.AddComponent( new EyeBall(Color.green) );

body.AddComponent( head );

...

Each composite object, or component, can be classified as a composite or a leaf.  A composite is a component that can contain other components.  In the above, HumanBody and Head are composites.  Conversely, EyeBall is a leaf because it does not contain other parts 1.

A disadvantage of the Composite pattern is that it makes it difficult to get a reference to a specific component instance.  The Patterns book calls this problem as an overly general design.  You can workaround this by assigning each component instance a unique identifier.  In the above code, for example, we might need a reference to the EyeBall object, but we might only have a reference to the HumanBody object.  We could add a method to HumanBody called GetPart, which accepts a component-id as an argument.  This method could then recursively checks if the id matches its own, or if it matches any of its children's ids.  Such functionality, however, is better managed in a separate Visitor class.

Visitor

Below is the Visitor's description:

Represent an operation to be performed on the elements of an object structure.  Visitor lets you define a new operation without changing the classes of the elements on which it operates. (Gamma 331)

The Visitor pattern separates the operations on an object structure from the object structure itself.  If a new operation--which applies to the entire object structure--needs to be added to the program, all you need to do is define a new Visitor class.

Visitor works hand-in-hand with the Composite pattern.  Continuing from the above code sample, let us invoke a Visitor operation on the HumanBody object structure:

...

AccumulateWeight visitor;

body.Accept(visitor);

System.out.println("The total weight of the human-body is: " + visitor.GetTotalWeight());

During the Accept operation, the visitor traverses every node object in the object structure and tallies up the HumanBody's weight, storing it in a member variable and providing access to it via the accessor GetTotalWeight (once the operation is complete).  We will discuss the specifics of how a Visitor performs this operation in more detail later.

Implementation

The following UML diagram depicts the object structure of the model in the sample project:

Composite Implementation

Let us begin by defining the architectural pattern classes, beginning with the Composite base classes SentenceComponent, SentenceComposite, SentenceLeaf.  For now, ignore the extends SentenceElement part.

NOTE: In the code, any "..." represents a section that has been omitted for clarity.

abstract class SentenceComponent extends SentenceElement
{
   SentenceComponent()
   {
      componentID = -1;
   }
   SentenceComponent(int componentID, final String type, 
                     final String value)
   {
      this.componentID = componentID;
      this.type = type;
      this.value = value;
   }
 
   // OPERATIONS: Accessors/mutators
   int GetComponentID()
   {
      return componentID;
   }
   void SetComponentID(int componentID)
   {
      this.componentID = componentID;
   }
   final String GetType()
   {
      return type;
   }
   void SetType(final String type)
   {
      this.type = type;
   }
   final String GetValue()
   {
      return value;
   }
   void SetValue(final String value)
   {
      this.value = value;
   }
   
   // OPERATIONS: Abstract
   abstract SentenceComponent Add(SentenceComponent component);
 
   protected int componentID;
   protected String value;
   protected String type;
}
 
class SentenceComposite extends SentenceComponent
{
   SentenceComposite()
   {
   }
   SentenceComposite(int componentID, final String type, 
                     final String value)
   {
      super(componentID, type, value);
   }
 
   // OVERRIDES: SentenceComponent
   SentenceComponent Add(SentenceComponent component)
   {
      if (component == null)
      {
         System.out.println("component is null, fiend.");
      }
      components.addElement(component);
      return component;
   }
 
   ...
 
   protected Vector components = new Vector();
}
 
class SentenceLeaf extends SentenceComponent
{
   SentenceLeaf()
   {
   }
   SentenceLeaf(int componentID, final String type, final String value)
   {
      super(componentID, type, value);
   }
 
   ...
 
   // OVERRIDES: SentenceComponent
   SentenceComponent Add(SentenceComponent component)
   {
      // empty method
      return component;
   }
}


The composite class SentenceComposite contains a collection of references to SentenceComponent objects.  As mentioned in the introduction, a composite is a component that can contain other components.  We have also defined a leaf class, SentenceLeaf.


This represents the bare-bones Composite framework infrastructure.  At this point, we are prepared to define the business classes WordGroup and Word, which extend the Composite framework:


class WordGroup extends SentenceComposite

{

   WordGroup()

   {

   }

   WordGroup(final String type, final String value)

   {

      super(-1, type, value);

   }


   WordGroup(int componentID, final String type, final String value)

   {

      super(componentID, type, value);

   }


   ...

}


class Word extends SentenceLeaf

{

   Word()

   {

   }

   Word(final String type, final String value)

   {

      super(-1, type, value);

   }


   Word(int componentID, final String type, final String value)

   {

      super(componentID, type, value);

   }


   ...

}


Now we can dynamically construct a SentenceComponent object model at runtime.  The following code does this:


WordGroup subject = new Subject(1, “subject”, “The tree”);

subject.AddComponent(new Word(11, “article”, “The”));

subject.AddComponent(new Word(12, “concrete noun”, “tree”));

Visitor Implementation


Try to think of the Composite object structure as a tree, where each node in the tree may or may not contain child nodes.  To read from or modify the contents of the tree, we invoke a traversal algorithm on the tree structure.  This algorithm governs how—in what order—the nodes of the tree are traversed.  Each time a node is visited by the algorithm, it is called a visit because the algorithm visits the node and performs some operation on it.


The traversal algorithm is defined by the set of classes constituting the (Composite) object structure, in the form of a method called Accept, which is declared on the abstract base class SentenceElement.  Note that SentenceComponent inherits from SentenceElement:


abstract class SentenceElement

{

   abstract void Accept(SentenceVisitor visitor);

}


It is convenient to provide a default implementation for Accept in the SentenceComposite class.  The Composite class will generally accept the visitor itself first, and then invoke Accept on all of its children.


   // OVERRIDES: SentenceElement

   void Accept(SentenceVisitor visitor)

   {

      visitor.VisitSentenceComposite(this);

      for (int i = 0; i < components.size(); i++)

      {

         SentenceComponent component =

            (SentenceComponent) components.elementAt(i);

         component.Accept(visitor);

      }

   }


A Visitor object encapsulates the operation to be performed over the object structure.  But rather than being a single operation for all nodes, the Visitor can handle each concrete node-type in the object structure differently.  The SentenceVisitor’s interface illustrates this:


abstract class SentenceVisitor

{

   abstract void VisitSentenceComposite(SentenceComposite component);

   abstract void VisitWordGroup(WordGroup component);

   abstract void VisitWord(Word component);

}


For example, let us say that we dynamically+ create the following Composite object structure at runtime:




We could then define a Visitor class which visits each object in the subject  Composite and dump the contents of each constituent object, based on that object’s type.


The output


+ WordGroup

- id = 1

- type = subject

- value = the tree


+ Word

- id = -1

- type = article

- value = the


+ Word

- id = -1

- type = concrete n.

- value = tree



would be produced by the following class


class _Dump extends SentenceVisitor

{

   // OVERRIDES: SentenceVisitor

   void VisitSentenceComposite(SentenceComposite component)

   {

      // empty method

   }

   void VisitWordGroup(WordGroup component)

   {

      System.out.println("+ WordGroup");

      System.out.println("  - id = " + component.GetComponentID());

      System.out.println("  - type = " + component.GetType());

      System.out.println("  - value = " + component.GetValue());

      System.out.println("");

   }

   void VisitWord(Word component)

   {

      System.out.println("+ Word");

      System.out.println("  - id = " + component.GetComponentID());

      System.out.println("  - type = " + component.GetType());

      System.out.println("  - value = " + component.GetValue());

      System.out.println("");

   }

}


When _Dump is invoked on subject, it dumps the information for Word and WordGroup objects slightly differently.  For WordGroup objects, it prefaces the property listing with the string, “+ WordGroup”, and “+ Word” for Word objects.


Below is the main controller code that dynamically builds the SentenceComponent Composite and then dumps it to the console with the SentenceVisitor Visitor.


public class App

{

   public static void main(String args[])

   {

      try

      {

         // Dynamically create the subject Composite object.

         WordGroup subject = new WordGroup(1, "subject", "The tree");

         subject.Add(new Word("article", "The"));

         subject.Add(new Word("concrete noun", "tree"));


         // Dump the contents of the subject Composite object to the

         // console.

         _Dump dump = new _Dump();

         subject.Accept(dump);


         // Dynamically create the predicate Composite object.

         WordGroup predicate = new WordGroup(1, "predicate", "was large");

         predicate.Add(new Word("linking verb", "was"));

         predicate.Add(new Word("adjective", "large"));


         // Dump the contents of the predicate Composite object to the

         // console.

         predicate.Accept(dump);

      }

      catch (Exception excep)

      {

         System.out.println(excep);

      }

   }

}


Conclusion


The Composite and Visitor design pattern combination offer a powerful way to build and manipulate an extensible object model.  As with all patterns, use them judiciously.



 

Works Cited



Gamma, Helm, Johnson, and Vlissides.  Design Patterns: Elements of Reusable Object-Oriented Software. Massachusetts: Addison-Wesley Longman, Inc. 1995.


Comments

1 Comments.
Share a thought or comment...
Comment 1 of 1
There seems to be something missing here: I think you should add a comment that each subclass of SentenceElement overrides the Accept method, not only the SentenceComponent class. I would add overriden Accept methods for all the subclasses to your example. This is how SentenceComposite implements the ACCEPT method: // OVERRIDES: SentenceElement void Accept(SentenceVisitor visitor){ visitor.VisitWordGroup(this); for (int i = 0; i < components.size(); i++){ SentenceComponent component = (SentenceComponent) components.elementAt(i); component.Accept(visitor); } } and this is how SentenceLeaf implements the ACCEPT method: // OVERRIDES: SentenceElement void Accept(SentenceVisitor visitor){ visitor.VisitWord(this); for (int i = 0; i < components.size(); i++){ SentenceComponent component = (SentenceComponent) components.elementAt(i); component.Accept(visitor); } }
Posted 16 years ago
 
Write a Comment...
...
Sign in...

If you are a member, Sign In. Or, you can Create a Free account now.


Anonymous Post (text-only, no HTML):

Enter your name and security key.

Your Name:
Security key = P1121A1
Enter key:
Article Contributed By Evan Egalite:
Visit Profile

 KB Article #100084 Counter
25881
Since 4/2/2008


©1995-2020 PrestwoodBoards  [Security & Privacy]
Professional IT Services: Coding | Websites | Computer Tech