Download presentation
Presentation is loading. Please wait.
1
Summary prepared by Kirk Scott
Chapter 5 Composite Summary prepared by Kirk Scott
2
Design Patterns in Java Chapter 5 Composite
Summary prepared by Kirk Scott
6
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
7
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
8
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
9
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
10
Book Definition of Pattern
The intent of the Composite pattern is to let clients treat individual objects and compositions of objects uniformly
11
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
12
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
13
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
14
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
15
The UML diagram on the following overhead illustrates the structure of the Composite design pattern
17
Structurally, this is the fundamental characteristic of the pattern:
The Composite class can have instances of Component, and Composite is a subclass of Component
18
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
19
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)
20
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
21
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
22
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
23
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
24
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
25
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
26
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
27
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
28
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
30
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?”
31
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.
32
[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.
33
[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.”
34
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?
35
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
36
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
37
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
38
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
40
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
41
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
42
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
43
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
44
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
45
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
46
Challenge 5.2 “Write the code for the getMachineCount() methods implemented by Machine and MachineComposite.”
47
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
48
Solution 5.2 “For the Machine class, getMachineCount() should be something like: public int getMachineCount() { return 1; }
49
The class diagram shows that MachineComposite uses a List object to track its components. To count the machines in a composite, you might write:
50
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].”
51
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
52
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
53
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
54
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
56
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
58
Solution 5.3 See the next overhead
60
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
61
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
62
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
63
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
64
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
66
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
67
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
68
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
69
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; }
70
The next overhead shows the UML diagram of the relationships between the objects created by the foregoing code
72
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
73
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
74
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
75
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
76
public int getMachineCount()
{ return 1; }
77
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
78
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
79
public int getMachineCount()
{ int count = 0; Iterator i = components.iterator(); while(i.hasNext()) MachineComponent mc = (MachineComponent) i.next(); count += mc.getMachineCount(); } return count;
80
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?”
81
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()); }
82
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
83
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
84
[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.”
85
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
86
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
87
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
88
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
89
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
90
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
91
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
92
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
94
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
95
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
96
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
97
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
98
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
99
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
100
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
101
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
102
isTree() Methods—Implementation and Declaration in MachineComponent
public boolean isTree() { return isTree(new HashSet()); } protected abstract boolean isTree(Set s);
103
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
104
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
105
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?
106
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
107
At this point, it should be clear that the non-implementation 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
108
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
109
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
110
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
111
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
112
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
113
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
114
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
115
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
116
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
117
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
118
Otherwise, the method should return true
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
119
Code for the isTree(set:Set) method in the MachineComposite class is given on the following overhead
120
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;
121
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
122
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 “tree-ness” is checking whether a node is visited more than once
123
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
124
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
125
The code for the implementation of the isTree() method for the Machine class is given on the following overhead
126
protected boolean isTree(Set visited)
{ visited.add(this); return true; }
127
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
129
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
131
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
132
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
133
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
135
UML for Processes The UML diagram on the following overhead shows how processes are modeled using the Composite design pattern
137
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
138
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
139
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
140
After disassembly, you make the shell again
This introduces a cycle into processing Such a cycle is considered normal, not a mistake
141
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
142
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
144
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.”
145
Solution 5.6 “Your solution should show the links in Figure B.4” [See the following overhead]
147
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
148
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
149
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
151
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
152
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
153
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);
154
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
155
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
156
public int getStepCount(Set visited)
{ visited.add(name); return 1; }
157
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
158
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
159
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
160
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
161
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;
162
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
163
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
164
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
165
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 non-tree structures are allowed
166
The End
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.