Gamma – Helm - Johnson – Vlissides
Many kinds of analysis require examining the text character by character. The text we need to analyze is
scattered throughout a hierarchical structure of glyph objects. To examine text in such a structure, we need an
access mechanism that has knowledge about the data structures in which objects are stored. Some glyphs might
store their children in linked lists, others might use arrays, and still others might use more esoteric data
structures. Our access mechanism must be able to handle all of these possibilities.
An added complication is that different analyses access information in different ways. Most analyses will
traverse the text from beginning to end. But some do the opposite—a reverse search, for example, needs to
progress through the text backward rather than forward. Evaluating algebraic expressions could require an
inorder traversal.
So our access mechanism must accommodate differing data structures, and we must support different kinds of
traversals, such as preorder, postorder, and inorder.
Encapsulating Access and Traversal
Right now our glyph interface uses an integer index to let clients refer to children. Although that might be
reasonable for glyph classes that store their children in an array, it may be inefficient for glyphs that use a linked
list. An important role of the glyph abstraction is to hide the data structure in which children are stored. That
way we can change the data structure a glyph class uses without affecting other classes.
Therefore only the glyph can know the data structure it uses. A corollary is that the glyph interface shouldn't be
biased toward one data structure or another. It shouldn't be better suited to arrays than to linked lists, for
example, as it is now.
We can solve this problem and support several different kinds of traversals at the same time. We can put
multiple access and traversal capabilities directly in the glyph classes and provide a way to choose among them,
perhaps by supplying an enumerated constant as a parameter. The classes pass this parameter around during a
traversal to ensure they're all doing the same kind of traversal. They have to pass around any information they've
accumulated during traversal.
We might add the following abstract operations to Glyph's interface to support this approach:
void First(Traversal kind)
void Next()
bool IsDone()
Glyph* GetCurrent()
void Insert(Glyph*)
Operations First, Next, and IsDone control the traversal. First initializes the traversal. It takes the kind of
traversal as a parameter of type Traversal, an enumerated constant with values such as CHILDREN (to traverse
the glyph's immediate children only), PREORDER (to traverse the entire structure in preorder), POSTORDER, and
INORDER. Next advances to the next glyph in the traversal, and IsDone reports whether the traversal is over or
not. GetCurrent replaces the Child operation; it accesses the current glyph in the traversal. Insert replaces
the old operation; it inserts the given glyph at the current position. An analysis would use the following C++
code to do a preorder traversal of a glyph structure rooted at g:
Glyph* g;
Página 64