Chapter 5
Composite
Summary prepared by Kirk Scott
1
Design Patterns in Java
Chapter 5
Composite
Summary prepared by Kirk Scott
2
3
4
5
Composites—The Introduction Before
the Introduction
• The composite design pattern is extremely
well-defined
• It is not ambigous
• It can be shown clearly with a straightforward
UML diagram
• The book’s definition, although interesting,
doesn’t really tell you what the pattern is
6
• There is no use in having an elaborate
introduction before the introduction, defining
terms, so that the definition will be clear
• However, one introductory comment might be
worthwhile
• At the beginning of the course, this general
observation was made:
• In a sense, data structures and algorithms can be
viewed as “micro” concerns about the
implementation internals of classes
7
• Correspondingly, design patterns can be viewed
as “macro” concerns about the relationships
between classes
• The composite design pattern shows that
although maybe useful, that distinction does not
apply in all cases
• The composite pattern, defined as relationships
among classes, is the basis for making tree-like
data structures of relationships between
instances of the classes
8
• Without further ado, the book’s definition will
simply be given on the following overhead
• This will be followed directly by an
explanation of what the pattern is, which the
definition doesn’t really make clear
9
Book Definition of Pattern
• Book definition:
• The intent of the Composite pattern is to let
clients treat individual objects and
compositions of objects uniformly
10
Concrete Explanation
• The composite design pattern is based on the
relationships between three classes/interfaces:
• Component
• Composite
• Leaf
• These things will be briefly defined
• Then the UML diagram will be shown
• Then more discussion will follow
11
Component
• The component is fundamental to the pattern
• A component is an abstract class or interface
• 1. It can be implemented by a class (Leaf) that
represents individual items
• 2. Or it can be implemented by a class
(Composite) that represents a collection of
such items
12
Composite
• A Composite contains a collection of
components as defined above
• This means that the composite can contain
individual items (leaves)
• It can also contain collections of items (other
composites)
• This is important: Composites can contain
instances of other composites
13
Leaf
• The Leaf is an implementation of Component
which consists of individual items
• The class is called Leaf because instances of
classes in these relationships form trees
• It is important to note that in this design
pattern, so-called leaf items are not restricted
to the outermost branches of the tree
14
• The UML diagram on the following overhead
illustrates the structure of the Composite
design pattern
15
16
• Structurally, this is the fundamental
characteristic of the pattern:
• The Composite class can have instances of
Component, and Composite is a subclass of
Component
17
Composite and Decorator
• If you think back to decorator, we’ve seen
something long these lines before
• A decorator class contained a reference to an
instance variable which was typed to a superclass
of the decorator
• Composite takes this idea so much further that it
results in something entirely different
• A composite can contain a collection of
references to objects which are typed to the
superclass
18
What Kind of Tree Does Composite
Form?
• Objects related in this way form a particular
kind of tree
• Do not be misled by the term “leaf”
• Leaves can be contained in internal nodes of
the tree
• Take a directory structure for example
• Any directory (a composite) can contain
individual files (leaves) as well as additional
directories (composites)
19
Operations
• Notice that the UML diagram had methods in
it named operation() and other()
• Components are either composites or leaves
• Behaviors (actions, operations, methods)
declared in the Component superclass have to
be defined/implemented in the subclasses so
that they make sense for both individual items
and collections of items
20
Clients
• The word client appears in the definition of
this pattern
• The design pattern provides a means of
making certain capabilities available to user
written code
21
• No client class is shown in the UML diagram
• In general, you would expect a client to have a
reference to a (concrete) composite
• That composite would be the root of a tree
• This descends through the “has-a” relationships
of the composite and the components it contains
• This is effectively a recursive situation, until you
reach contained items which are leaves only
22
The Component Class Defines a
(Generic, User) Interface
• The Component class is shown in the UML
diagram as an abstract class
• The Component class could also be
implemented as an interface
• The Component class provides a generic
interface that the Leaf class and the
Composite class have to conform to
23
•
•
•
•
It is this interface that the client code would use
The basic idea is this:
Client code can call a defined operation on a leaf
It can also call a defined operation on a
composite
• This is what the book definition means when it
says that the pattern allows clients to treat
individual objects and compositions of objects
uniformly
24
The Definition, Again
• As stated in the definition, the intent of the
Composite pattern is to let clients treat
individual objects and compositions of objects
uniformly
• It is the Component part of the design that
defines this uniform interface
• It is the subclasses that implement it
25
• Since Component is implemented as an
abstract class is, it can contain some
inheritable method implementations
• The subclasses, Leaf and Composite, may have
methods of their own, may override inherited
methods, and have to implement abstract
methods
26
The Pattern and Polymorphism
• Interfaces, polymorphism, and dynamic binding
play a key role in this design pattern
• An instance of Composite can contain more than
one different kind of thing
• What it can contain is defined by the Component
• A given method can be called on the contents of
a composite
• Different versions of the method will be used
depending on whether the element is a leaf or
another composite
27
Book Challenge
• The UML diagram is repeated on the next
overhead for reference in the challenge that
follows
• The challenge will be easy and repetitive if the
foregoing explanation was clear
28
29
• Challenge 5.1
• “Why does the Composite class in Figure 5.1
maintain a collection of Component objects
instead of simply a collection of leaves?”
30
• Solution 5.1
• “Designing the Composite class to maintain a
collection of Component objects lets a
Composite object hold either Leaf objects or
other Composite objects.
31
• [Solution 5.1, cont’d.]
• In other words, this design lets us model groups
as collections of other groups.
• For example, we might want to define a user’s
system privileges as a collection of either specific
privileges or other groups of privileges.
• As another example, we might want to be able to
define a work process as a collection of process
steps and other processes.
32
• [Solution 5.1, cont’d.]
• Such definitions are much more flexible than
defining a composite to be a collection of
leaves.
• If we allowed only collections of leaves, our
“composites” could be only one layer deep.”
33
• Comment mode on:
• This last part of the book’s answer raises a
question, which will be stated below, but not
pursued
• Could you come up with a design pattern that
created multi-layer trees where there were
internal nodes and leaf nodes, but only the
lowest layer of internal node code have a leaf
node?
34
• Comment mode cont’d.
• A directory structure is probably the example
of the pattern most familiar to computer
people
• The pattern also arises in manufacturing
(databases) where assemblies consist of parts
and subassemblies
35
Oozinoz Example
• The book, as usual, has an example from the
Oozinoz domain
• Briefly, there are individual machines that are
components of the manufacturing process
• These are the leaves
36
• There are also machines which are
compositions of other machines
• These compositions are collectively thought of
as single, composite components of the
manufacturing process
• Any given machine, i.e., component, may
consist of a combination of single machines or
composite machines
37
• The concrete example is given in the following
UML diagram
• Note that the diagram includes a method,
getMachineCount()
• The details of the pattern will become
apparent when deciding how to implement
this method
38
39
• Note that in the diagram the superclass,
MachineComponent, is shown in italics
• MachineComponent is an abstract class
• The method getMachineCount() is also in
italics and abstract
• The subclasses have to implement this
abstract method
• They might also inherit useful concrete
methods
40
• Note also that the implementation of
MachineComposite is based on the Java
collection class List
• The MachineComposite class has an instance
variable which is a list containing references to
the components that an object of the class
contains
• Compare this with cups containing ArrayLists
of seeds
41
Component Method Implementation
• In general, when implementing methods in
the subclasses, the implementation for a leaf
will be a “one shot deal”
• It is reasonable to expect that implementation
for the composite class will involve iterating
over the elements of the component list
• Because the two classes have a common
interface, they will both implement methods
with the same name
42
• In the implementation of the method in the
composite, when iterating over the
component list, the list may contain
references to both leaves and composites
• When iterating over the elements of the list,
the same method name can be called on each
element
43
• Polymorphism and dynamic binding make this
possible
• If an element of the list is a leaf, Machine,
calling the method will cause the leaf version
of the method to be used
• If an element of the list is a composite,
MachineComposite, calling the method will
cause the composite version of the method to
be used
44
• In this example, elements of the list will be
typed MachineComponent and the method in
question is getMachineCount()
• The Machine class will have a version of the
method that simply returns the value 1
• The MachineComposite class will have a
version of the method that iterates over its
components list, calling the method on its
elements, accumulating a count
45
• Challenge 5.2
• “Write the code for the getMachineCount()
methods implemented by Machine and
MachineComposite.”
46
• Comment mode on:
• It should be apparent that getMachineCount()
simply returns 1 for a single, simple instance of
the Machine class
• As suggested by the earlier discussion, the
implementation in the MachineComposite class
will involve iterating over its list of components,
accumulating the return values of recursive calls
to getMachineCount() on them
47
• Solution 5.2
• “For the Machine class, getMachineCount()
should be something like:
• public int getMachineCount()
• {
•
return 1;
• }
48
• The class diagram shows that
MachineComposite uses a List object to track
its components. To count the machines in a
composite, you might write:
49
• public int getMachineCount()
• {
•
int count = 0;
•
Iterator i = components.iterator();
•
while(i.hasNext())
•
{
•
MachineComponent mc = (MachineComponent)
i.next();
•
count += mc.getMachineCount();
•
}
•
return count;
• }
• If you’re using JDK 1.5 [or later], you may have used an extended for loop
[a for each loop].”
50
The Composite Design Pattern and
Recursion
• Notice that the MachineComposite
implementation of getMachineCount() is both
iterative and recursive at the same time
• For any given node, in essence, you iterate
over its children
• On each child you make a recursive call to get
its machine count
51
• In the case of a simple machine, there is no
further recursion
• In the case of a composite, the pattern is
repeated of iterating over the children getting
the machine count
52
• In the recursion you iterate over list elements
that are typed to the superclass,
MachineComponent
• The counting method is called on these
objects, whether Machines or
MachineComposites, until there is no
composite left to disassemble
• Then all that will trickle back up are sums of
integer counts
53
Other Methods that might be
Implemented
• The book suggests a few more methods that
might be added to the composite example
based on machines
• They are given in the following table
54
55
• The book observes that you would expect the
implementation of each of these methods in
the MachineComposite class to be recursive
• This is not a surprise, considering that the
implementation of getMachineCount() was
recursive
• The book gives challenge 5.3 in the form of
the table on the next overhead
56
57
• Solution 5.3
• See the next overhead
58
59
Composites, Trees, and Cycles
• The book seems to make this a little more
complicated than necessary
• In most cases, it seems likely that the logic of a
composite would require that it be a tree
• If this is the case, then it might be helpful to
make sure the code included implementation
of the functionality described on the next
overhead
60
• At the very least, include a method or
methods that made it possible to check
whether a created structure was a tree
• Or, include code in method implementations
that disallowed adding nodes in such a way
that a non-tree structure resulted
61
• On the other hand, maybe the code doesn’t
prevent the creation of non-tree structures
and something that isn’t tree-like is either
desirable, or could result inadvertently
• In this case, the implementations of methods
like getMachineCount() would have to include
code that made sure that iteration only
counted elements once, even if the structure
containing them was not a tree
62
Examples that aren’t Trees
• In general, the presence of some sort of cycle
in the data structure would mean that it
wasn’t really a tree
• The book introduces some notation and
simple graph theory in order to define trees
and talk about cycles
• Objects in UML are represented by rectangles,
and references between them are represented
by solid lines with open arrowheads
63
• The UML diagram on the following overhead
shows a simple directed graph, which happens
to be a path (a sequence of references from
one object to another)
• This example clearly does not contain a cycle
64
65
• A graph is a tree if:
• It has one node, a root node, that has no
references to it
• And every other node has only one parent node,
i.e., only one node that refers to it
• Notice how in this terminology arrows point from
the top down
• This is different from the notation for inheritance,
where the subclasses refer to their parents
66
• The term cycle refers to a sequence of
references where, starting at a given node,
after following the sequence, you arrive at the
same node again
• Any data structure that contains a cycle can’t
be a tree
67
An Example without a Cycle that isn’t a
Tree
• Consider the code shown on the next overhead
• Assume that you can construct instances of
different kinds of simple and composite machines
• Also assume that there is an add() method that
implements the navigability relationship shown
by the open arrowheads in UML object diagrams
• There is no need to try and trace out what the
code does
• That is immediately shown in the diagram that
follows it
68
•
•
•
•
•
•
•
•
•
•
•
•
•
•
public static MachineComposite plant()
{
MachineComposite plant = new MachineComposite(100);
MachineComposite bay = new MachineComposite(101);
Machine mixer = new Mixer(102);
Machine press = new StarPress(103);
Machine assembler = new ShellAssembler(104);
bay.add(mixer);
bay.add(press);
bay.add(assembler);
plant.add(mixer);
plant.add(bay);
return plant;
}
69
• The next overhead shows the UML diagram of
the relationships between the objects created
by the foregoing code
70
71
• This is probably not a desirable situation
• Logically, it would seem to make sense for a
given object (the mixer) to be referred to only
by its immediate parent (bay) not its ultimate
parent (plant)
• However, the point is that it is possible to
create this structure, and it’s necessary to be
able to deal with it
72
• It should be apparent that the result is not a tree
• It is somewhat unfortunate that the book has
discussed “non-tree-ness” in terms of cycles in
directed graphs
• As a directed graph, this diagram doesn’t show a
cycle
• However, the mixer:Machine object in the
diagram can be reached twice, via different,
linked paths
73
• In other words, mixer:Machine has two parent
nodes, the List belonging to
plant:MachineComposite and the List belonging
to bay:MachineComposite
• For this reason the structure is not a tree, even
though as a directed graph it doesn’t have a cycle
• If the graph were represented in non-directed
form, then plant to bay to mixer to plant would
essentially be a cycle
74
Method Code when Concerned with
Cycles
• At any rate, such a structure is possible, and
the task is to write correct implementations of
the getMachineCount() method
• The straightforward implementation of the
getMachineCount() method for a simple
machine shown on the next overhead should
still be correct
75
• public int getMachineCount()
• {
•
return 1;
• }
76
• In this situation the code given for a
composite would not be correct
• Suppose a given node object can be reached
twice, by different paths
• The simple, recursive getMachineCount()
method for MachineComposite given earlier
would give a wrong answer
77
• The code is repeated on the next overhead for
reference
• Suppose it was run on the structure where the
mixer had links from two parents
• The mixer would be counted twice, and the
machine count returned by the method would
be 1 greater than the correct answer
78
• public int getMachineCount()
• {
•
int count = 0;
•
Iterator i = components.iterator();
•
while(i.hasNext())
•
{
•
MachineComponent mc =
(MachineComponent) i.next();
•
count += mc.getMachineCount();
•
}
•
return count;
• }
79
• The book concretely illustrates the assertion
concerning the current version of the code
with a challenge
• Challenge 5.4
• “What does the program on the following
overhead print out?”
80
•
•
•
•
•
•
•
package app.composite;
import com.oozinoz.machine.*;
public class ShowPlant
{
public static void main(String[] args)
{
MachineComponent c =
OoozinozFactory.plant();
•
System.out.println(“Number of
machines: “ + c.getMachineCount());
•
}
• }
81
• Comment mode on:
• The program shown on the previous overhead
creates the non-tree structure diagrammed
earlier
• It then calls the getMachineCount() method
on the structure
82
•
•
•
•
Solution 5.4
“The program prints out
Number of machines: 4
There are, in fact, only three machines in the
plant factory, but the mixer is counted by both
plant and bay.
• Both of these objects contain lists of machine
components that refer to the mixer
83
• [Solution 5.4, cont’d.]
• The results could be worse.
• If, say, an engineer adds the plant object as a
component of the bay composite, a call to
getMachineCount() will enter an infinite loop.”
84
• Comment mode on:
• Notice what they’re saying here
• This example was not a tree, but it also did
not contain a cycle
• As a result, it gave an incorrect count
85
• Their second, verbal scenario, created a cycle
• That’s when you would enter an infinite loop
• From the point of view of the data structure
created, this is a loop through the cycle
• From the point of view of the code, because
the method implementation is recursive, this
structural loop would cause an infinite
sequence of recursive calls
86
Writing Correct Code for Handling
Non-Tree Data Structures
• An application that allows structures to be
created could check whether a given node
already exists in the structure
• If it does, it would not be added/linked in
again
• Checking to see whether a node already exists
would be based on maintaining a set of
existing nodes
87
• It would also be possible to check a structure
after the fact to see whether duplicate nodes
have been added/linked in
• The book introduces a method named isTree()
in the Component class
• isTree() traverses a data structure, checking to
see whether any node is encountered more
than once
• If so, then isTree() returns false
88
• isTree() could be called before and after
adding a node
• If the return value changed from true to false,
then you would remove the node that was
just added
• This would prevent the formation of a
structure that wasn’t a tree
89
• On the other hand, in some domains, non-tree
structures might be allowed
• In that case, isTree() would still be a useful
addition to a set of methods
• It would tell you what kind of structure you
were dealing with at any given time
90
Implementing the New Approach
• The goal is to be able to check whether a
structure is a tree
• Let components have id’s
• Let an isTree() method be added
• The implementation of isTree() makes use of
the Java Set class
• Remember that a set can’t contain duplicate
elements
91
• A UML diagram for this is given on the following
overhead
• The MachineComponent class shows two isTree()
methods, one abstract and one concrete
• One takes a set parameter and one doesn’t, so
they’re overloaded
• This turns out to be a useful technique for
inheriting as much as possible out of the
superclass
• How it works will be described after the diagram
92
93
How the Methods in the New Diagram
Interact
• The component class has two isTree()
methods, one abstract and one concrete
• The abstract method takes a parameter
• The concrete method does not
• Doing the methods this way is not an inherent
aspect of the design pattern
• However, it is useful, and how it works needs
to be understood
94
• We have seen similar kinds of things in some
other patterns
• There is an interplay between the concrete
method that is inherited and the abstract
method that is implemented in the subclasses
• How they work together is an application of
polymorphism and dynamic binding
95
• This combination of abstract and concrete
methods has a beneficial effect
• It minimizes the number of different places
where the same method signature has to be
implemented in the set of classes
• Specifically, it makes the isTree() method
without a parameter available to clients for
use with both leaves and composites
96
• The implementation of isTree() without a
parameter depends on the implementation of
isTree() with a parameter
• isTree() with a parameter is implemented
differently in the Leaf and Composite classes
• The basic idea underlying isTree()/isTree(s:Set)
comes down to two points, given on the
following overhead
97
• 1. In order to tell whether something is a tree,
you have to maintain a set of nodes and call a
method that takes such a set of nodes as a
parameter
• 2. However, from client code, for example,
you would like to simply be able to call isTree()
without having to pass a set parameter
98
• There is no reason why client code should be
concerned with creating a set for checking
whether something is a tree
• The classes of the design pattern take care of
implementing versions which require a set
parameter
• This is completely hidden from client code
99
The Structure Shown in the UML
Diagram, Again
• In the superclass, MachineComponent, there
is an abstract method isTree(set:Set)
• The subclasses have to implement this
version, which takes a set parameter
• In the superclass, MachineComponent, there
is also a concrete method isTree()
• The subclasses inherit this version, which
doesn’t take a parameter
100
• The implementation of the concrete version of
the isTree() method in MachineComponent
contains a call to the abstract version
• It is within the implementation of isTree() without
a parameter that the needed set is created and
passed as a parameter
• The implementation of isTree() and the
declaration of isTree(set:Set) in
MachineComponent are shown on the following
overhead
101
isTree() Methods—Implementation and
Declaration in MachineComponent
• public boolean isTree()
• {
•
return isTree(new HashSet());
• }
• protected abstract boolean isTree(Set s);
102
• The subclasses implement the abstract version
concretely
• Client code will call the version without a
parameter on something typed to the abstract
superclass, MachineComponent
• Polymorphism and dynamic binding mean that
what happens depends on the implementation of
isTree() with a set parameter for the actual object
type, either Leaf or Composite
103
Delegation
• The book refers to this relationship between
methods as delegation
• The MachineComponent code delegates an
isTree() call to its abstract isTree(s:Set) method
• In other words, the isTree() implementation
contains a call to isTree(set:Set)
• As noted, polymorphism and dynamic binding
make delegation work
104
Dwelling on This at Greater Length
• If the foregoing explanations weren’t
sufficient, this is what should be on your mind
• There is no implementation of the abstract
method
• What does it mean, then, to call the abstract
method in the implementation of the concrete
method?
• Is this allowed? If so, how does it work?
105
• The isTree(set:Set) method is abstract—but
for this reason, the class that contains it is
abstract
• This means that there can be no instances of
the abstract class
• As a consequence, the only real cases where
the concrete method is called are when it is
inherited and called on instances of the
subclasses
106
• At this point, it should be clear that the nonimplementation of the abstract version of the
method in the superclass is not a problem
• The subclasses are obligated to provide
implementations of the abstract method
107
• When the inherited concrete method is called
on a subclass object, the version of the
method that takes a parameter, which is called
in the body of that method, will be the one
defined in the subclass
• The fact that this scheme works is the result of
polymorphism and dynamic binding
108
• It might be worth noting that delegation has
something in common with a callback
sequence
• You define an isTree(set:Set) method in the
subclasses
• Client code never calls this directly
• However, when client code calls isTree()
without a parameter, the corresponding
version with a parameter is called
109
Reiterating the Desirable Outcome
that Results from This
• What is accomplished by this?
• It is possible to call isTree() without a
parameter on things that are typed
MachineComponent, namely objects that are
either Machine and MachineComposite
• In other words, the desired interface for client
code is isTree() without a parameter
110
• Internally, isTree(set:Set) with a parameter is
needed in order to support the logic of
checking whether an object is a tree
• The subclasses only need to implement
isTree(set:Set)
• They simply inherit the version of isTree()
without a parameter
111
• In theory, in an application program you could
directly call the versions that take a parameter
• That would mean providing a parameter
• There is no practical reason for doing this
112
• In practice, the versions that take a parameter
would only be called as a result of an initial
call to the version that doesn’t take a
parameter
• This is the version inherited from the
superclass, where the implementation
includes the construction of the needed set
parameter
113
Method Implementations in the
Subclasses
• It’s necessary to provide subclass
implementations of the abstract
isTree(set:Set) method that takes a set
parameter
• The implementation for the simple Machine
class is not recursive
• The implementation for the
MachineComposite class is recursive
114
• Even though the implementation for
MachineComposite is more complicated than
the implementation for Machine, it makes
sense to do examine MachineComposite first
• You see the algorithm for determining
whether something is a tree
• You confront the recursion
• Then the implementation in Machine is just
the base case
115
The Implementation of isTree(set:Set)
in MachineComposite
• Remember that the MachineComposite class
contains a List of all its components
• The set parameter in the isTree(set:Set)
method of MachineComposite deals with a set
of visited node objects
• In the discussion that follows, do not confuse
these two different collections
116
• The MachineComposite implementation of
isTree(set:Set) adds the object it is called on to
the visited set
• The isTree(set:Set) method for
MachineComposite iterates over the
composite’s components
• In that iteration, isTree(set:Set) is called on
each component
117
• The isTree() method will return false under
either of these two conditions:
– Either the component that the current call was
made on is already in the visited set
– Or a recursive call to isTree(set:Set) on one of the
components elements returns false
• Otherwise, the method should return true
118
• Code for the isTree(set:Set) method in the
MachineComposite class is given on the
following overhead
119
•
•
•
•
•
•
•
•
•
•
•
•
protected boolean isTree(Set visited)
{
visited.add(this);
Iterator i = components.iterator();
while(i.hasNext())
{
MachineComponent c = (MachineComponent) i.next();
if(visited.contains(c) || !c.isTree(visited))
return false;
}
return true;
}
120
The Implementation of isTree(set:Set)
in Machine
• The implementation contains two lines of
code:
• 1. It adds the current (leaf) node to the
incoming set of visited nodes
• 2. It then returns true
121
1. Adding the Node to the Set
• By definition, isTree(set:Set) has to deal with
the set parameter
• The isTree(set:Set) method may be called on a
leaf as a result of recursive calls to composites
which contain that leaf
• The overall algorithm for checking for “treeness” is checking whether a node is visited
more than once
122
• Each leaf/Machine has to be added to the set
so that elsewhere in the recursive calls,
multiple references to it could be detected
• Keep in mind that the set parameter is a
reference, so after returning from this call, the
addition of the object is reflected in the set
that belongs to the calling method above it
123
2. Returning True
• A call to this version of the method belonging
to leaves is the base case of the recursion
• A single machine by itself always satisfies the
definition of a tree
• Therefore, having added the leaf/Machine to
the set of visited nodes, it always returns true
124
• The code for the implementation of the
isTree() method for the Machine class is given
on the following overhead
125
• protected boolean isTree(Set visited)
• {
•
visited.add(this);
•
return true;
• }
126
Another Example
• A UML diagram for another example is given
on the following overhead
• It is based on cups and seeds
• It completely parallels the diagrams given so
far
• There is just one diagram for this design
pattern
127
128
Lasater’s UML
• Lasater’s UML diagram is given on the following
overhead
• It completely parallels the book’s initial generic
example
• It has the advantage that it includes a client
• It also shows in greater detail what methods
might be desirable in a client
• It also has a comment which makes it clear that
method implementations would be based on
iteration
129
130
Composites with Cycles--Comment
• The rest of the chapter is on the topic of
composites with cycles
• If there is time, it will be covered in class
• Otherwise, it will be up to students to read
the sections in the book and the remainder of
these overheads on their own
• If the syllabus for the current semester
includes 2 days for Composite, it is likely that
these overheads will be covered in class
131
Composites with Cycles
• Earlier, the book gave an example of a
machine composite where a mixer had two
parents
• Such a structure is likely an accident
• Letting the mixer have two parents is a
mistake to be avoided in modeling the
situation
• However, there are problem domains where
cycles may be a correct part of the model
132
• The book returns to Oozinoz for an example
• This time the topic is modeling the
manufacturing process rather than modeling
the factory machines
• For the sake of background, the book
describes some of the manufacturing process
• It also provides the following illustration of a
fireworks shell
133
134
UML for Processes
• The UML diagram on the following overhead
shows how processes are modeled using the
Composite design pattern
135
136
• At the top of the hierarchy is the
ProcessComponent class
• Under it is the ProcessStep class
• A ProcessStep would be a single, discrete step
in the manufacturing process
137
• Under the ProcessComponent class is also the
ProcessComposite class
• A component of a process may be an instance
of ProcessComposite, which contains a List of
constituent subprocesses
• The ProcessComposite class also has two
subclasses, ProcessAlternation and
ProcessSequence
138
Reworking Defective Shells
• After shells are manufactured, they are
inspected
• If they do not pass quality control, they have
to be reworked
• In the Oozinoz world, you don’t just toss the
ingredients back in the bin
• You disassemble the shell, apparently saving
as much completed work as possible
139
• After disassembly, you make the shell again
• This introduces a cycle into processing
• Such a cycle is considered normal, not a
mistake
140
• Figure 5.8 is shown on the overhead following
the next one
• The book refers to it as an object model
• It is an incomplete diagram showing the
manufacturing process as a sequence of
objects with references to each other
141
• The reworkOrFinish subprocess takes one of
two alternative paths, either disassembly
followed by make, or finish
• The phrase “disassembly followed by make”
reveals the potential cycle in the
manufacturing process
• Manufacturing starts with make, and in the
case of a defective shell, starts over again with
make
142
143
• Challenge 5.6
• “Figure 5.8 shows the objects in a model of
the shell assembly process. A complete object
diagram would show links between any
objects that refer to each other. For example,
the diagram shows the references that the
make object retains. Your challenge is to fill in
the missing links in the diagram.”
144
• Solution 5.6
• “Your solution should show the links in Figure
B.4”
• [See the following overhead]
145
146
• Comment mode on:
• Some of the boxes have more than one arrow
exiting from them
• This represents alternative outcomes and
paths during the manufacturing process
• The completed diagram contains a cycle
• The boldface arrows highlight this path of
object references
147
Correct Code Implementations When
Cycles Are Possible
• In the earlier example the book worked with
an isTree() method
• The implicit idea was that having a tree-like
structure was desirable
• The isTree() method would make it possible to
detect when something was “wrong”
• Something wrong would cause the node count
to be wrong, for example
148
• Now the book wants to consider how to
implement methods correctly if cycles are
allowed in the structure
• Before considering the code example, it is
helpful to look again at the outline of the
methods provided in the UML diagram of
processes given earlier
149
150
The getStepCount() method
• The getStepCount() method in this example is
somewhat analogous to the getMachineCount()
• Instead of counting all instances of steps,
getStepCount() is intended to count only the
distinct leaf nodes in a path
• Since cycles may be present, any given path may
be “endless”
• Even so, it will only contain a finite number of
distinct leaf nodes
151
• The implementation of getStepCount() is
similar to getMachineCount in the overall
approach to implementation
• There is a version which takes a Set parameter
and a version which doesn’t
• The version that doesn’t works by delegation
to the version that does
152
• In other words, this would be part of the
implementation of the component class:
•
•
•
•
•
public int getStepCount()
{
return getStepCount(new HashSet());
}
public abstract int getStepCount(Set visited);
153
getStepCount() for a Single Process
Step
• The implementation of the version of the
method that takes the parameter is simple for
the single ProcessStep class
• It will be shown on overhead following the
next one
• It only adds the name of the leaf to the list,
not the object itself
154
• The method adds the name, not the object, to
the set because it isn’t supposed to count all
of the objects in a path, but the distinct leaf
nodes in a path
• Various process components may be repeated
in a single manufacturing path
• If the repeated components have the same
name, then the nodes they contain will not be
counted again
155
• public int getStepCount(Set visited)
• {
•
visited.add(name);
•
return 1;
• }
156
getStepCount() for a Composite
Process
• The version of the getStepCount(Set visited)
method for the ProcessComposite class is
recursive
• The getMachineCount() method for the
MachineComposite class was recursive
• However, getMachineCount() method did not
protect against cycles
157
• The implementation of the getStepCount()
method has to produce the correct result even
if a cycle is present in the process composite
• Like the version of the method for a simple
Process, this method will work with names of
processes in the visited list
• The structure of the method will not be
exactly the same as the structure of the
getMachineCount() or isTree() methods
158
• The code for the method is shown on the
overhead following the next one
• In addition to the differences noted already,
the implementation is also different because it
uses a for loop rather than iteration
• This is simply an alternative approach to
iterating over the components of a composite
159
• The code is not especially easy to read, but it
can be summarized in this way:
• You are counting recursively, like in the initial
getMachineCount() example
• However, the logic of isTree() is also present
• You don’t count something if it’s in the set of
visited items and you’ve already counted it
before
160
• public int getStepCount(Set visited)
• {
•
visited.add(getName());
•
int count = 0;
•
for(int i = 0; i < subprocesses.size(); i++)
•
{
•
ProcessComponent pc = (ProcessComponent)
subprocess.get(i);
•
if(!visited.contains(pc.getName()))
•
count += pc.getStepCount(visited);
•
}
•
return count;
• }
161
Consequences of Cycles
• Some operations, like counting distinct leaves,
may make sense in a structure with cycles
• In such a case, you have to make sure that you
don’t “hit” the same node twice
• Other operations may simply make no sense
for structures with cycles
162
• You can’t compute the length of a path, for
example
• The path with a cycle is endless, and any
method to do this computation will loop/recur
endlessly
• It will also not be possible to implement any
method which relies on the existence of a
single parent for every node in the structure
163
Summary
• The Composite design pattern contains two
concepts
• A group can contain individual items or groups
of items
• The pattern makes sure that the individual
items and the groups of items share a
common interface
• This can be done by means of an abstract class
or a Java interface
164
• The Composite design pattern can easily lead
to recursive method implementations
• If that is the case, then you can take steps to
make sure that structures are always trees
• You can also take steps to make sure that
method implementations will still work if nontree structures are allowed
165
The End
166
© Copyright 2026 Paperzz