The list structure is one of the most fundamental data structures in programming. The following is a common definition of a list.
We rephrase the above as follows.
We model this abstract notion of a list by an abstract class called AList, the empty list and the non-empty lists as a concrete subclasses of AList called EmptyList and NEList respectively. The following UML diagram illustrates this structural design.
In the above diagram, the methods getFirst() and getRest() in AList are pure abstract, that is they contain no code body. They are overriden in the concrete subclasses NEList and EmptyList with concrete code body to carry out the appropriate tasks. The client should program only to the abstract class AList. Polymorphism will properly dispatch the method call to the appropriate concrete subclass at run-time.
The EmptyList class is implemented with the singleton pattern to model the fact that there is a unique empty list.
An NEList object "has a" an element, called rest, whose structure is isomorphic to the enclosing NEList object itself.. NEList is said to be a composite. Its structure is recursively constructed and terminates with the "base case", the EmptyList. The above taxonomy is an example of what is called the composite design pattern. The composite pattern is a structural pattern that prescribes how to build a container object that is composed of other objects whose structure is isomorphic to that of the container itself. In this pattern, the container is called a composite. The composite pattern also prescribes a coding pattern for the container's methods: when a container is called to perform an operation, it iterates through its list of composed objects and call on them to perform the same operation. It allows the client to treat the container and what it contains uniformly by making use of polymorphism. The following UML diagram illustrates the general composite pattern.
In many situations, an operation may call on "helper" operations on the sub-components to carry out the task. The coding pattern for an operation that requires a helper is as follows.
class Composite extends AComponent {
// fields and sub-components.
public Object doTask (Object input) {
// Process the input parameter and the data fields to obtain a temporary
result needed by the sub-components to help complete the task:
Object temp = process (data fields, input);
// Pass the temporary result to each of the sub-components asking each of
them to "help" complete the task:
Object pr1 = _part1.helpDoTask (temp); //
partial result 1.
Object pr2 = _part2.helpDoTask
(temp);
// etc...
Object prN = _partN.helpDoTask
(temp);
//
Reassemble the partial results computed by the sub-components to obtain the
final result:
return putTogether (pr1, pr2, ..., prN);
}
protected Object helpDoTask (Object input) {
// Process the input parameter and the data fields to obtain a temporary
result needed by the sub-components to help complete the task:
Object temp = processPartial (data fields, input);
// Pass the temporary result to each of the sub-components asking each of
them to "help" complete the task:
Object pr1 = _part1.helpDoTask (temp); //
partial result 1.
Object pr2 = _part2.helpDoTask
(temp);
// etc...
Object prN = _partN.helpDoTask
(temp);
//
Reassemble the partial results computed by the sub-components to obtain the
final result:
return putTogetherPartial (pr1, pr2, ..., prN);
}
}
class Basic extends AComponent {
// fields
public Object doTask (Object input) {
// Process the input parameter and the data fields to obtain the result:
return simpleProcess (data fields, input);
}
protected Object helpDoTask (Object input) {
// Process the input parameter and the data fields to obtain the result:
return simpleProcessPartial (data fields, input);
}
}