Ensuring Technical Readiness For Copilot in Microsoft 365
Gof Design Pattern
1. A design pattern names, abstracts, and identifies the key aspects of a common design structure that
make it useful for creating a reusable object‐oriented design. The design pattern identifies the
What is a Design Pattern? participating classes and instances, their roles and collaborations, and the distribution of responsibilities.
Christopher Alexander says, quot;Each pattern describes a problem which occurs over and over again in our Each design pattern focuses on a particular object‐oriented design problem or issue. It describes when it
environment, and then describes the core of the solution to that problem, in such a way that you can applies, whether it can be applied in view of other design constraints, and the consequences and trade‐
use this solution a million times over, without ever doing it the same way twicequot; [AIS+77, page x]. Even offs of its use. Since we must eventually implement our designs, a design pattern also provides sample
though Alexander was talking about patterns in buildings and towns, what he says is true about object‐ C++ and (sometimes) Smalltalk code to illustrate an implementation.
oriented design patterns. Our solutions are expressed in terms of objects and interfaces instead of walls
Although design patterns describe object‐oriented designs, they are based on practical solutions that
and doors, but at the core of both kinds of patterns is a solution to a problem in a context.
have been implemented in mainstream object‐oriented programming languages like Smalltalk and C++
In general, a pattern has four essential elements: rather than procedural languages (Pascal, C, Ada) or more dynamic object‐oriented languages (CLOS,
Dylan, Self). We chose Smalltalk and C++ for pragmatic reasons: Our day‐to‐day experience has been in
The pattern name is a handle we can use to describe a design problem, its solutions, and consequences
these languages, and they are increasingly popular.
in a word or two. Naming a pattern immediately increases our design vocabulary. It lets us design at a
higher level of abstraction. Having a vocabulary for patterns lets us talk about them with our colleagues, The choice of programming language is important because it influences one's point of view. Our
in our documentation, and even to ourselves. It makes it easier to think about designs and to patterns assume Smalltalk/C++‐level language features, and that choice determines what can and
communicate them and their trade‐offs to others. Finding good names has been one of the hardest cannot be implemented easily. If we assumed procedural languages, we might have included design
parts of developing our catalog. patterns called quot;Inheritance,quot; quot;Encapsulation,quot; and quot;Polymorphism.quot; Similarly, some of our patterns are
supported directly by the less common object‐oriented languages. CLOS has multi‐methods, for
The problem describes when to apply the pattern. It explains the problem and its context. It might
example, which lessen the need for a pattern such as Visitor (page 331). In fact, there are enough
describe specific design problems such as how to represent algorithms as objects. It might describe class
differences between Smalltalk and C++ to mean that some patterns can be expressed more easily in one
or object structures that are symptomatic of an inflexible design. Sometimes the problem will include a
language than the other. (See Iterator (257) for an example.)
list of conditions that must be met before it makes sense to apply the pattern.
The solution describes the elements that make up the design, their relationships, responsibilities, and
Describing Design Patterns
collaborations. The solution doesn't describe a particular concrete design or implementation, because a
How do we describe design patterns? Graphical notations, while important and useful, aren't sufficient.
pattern is like a template that can be applied in many different situations. Instead, the pattern provides
They simply capture the end product of the design process as relationships between classes and objects.
an abstract description of a design problem and how a general arrangement of elements (classes and
To reuse the design, we must also record the decisions, alternatives, and trade‐offs that led to it.
objects in our case) solves it.
Concrete examples are important too, because they help you see the design in action.
The consequences are the results and trade‐offs of applying the pattern. Though consequences are
We describe design patterns using a consistent format. Each pattern is divided into sections according to
often unvoiced when we describe design decisions, they are critical for evaluating design alternatives
the following template. The template lends a uniform structure to the information, making design
and for understanding the costs and benefits of applying the pattern. The consequences for software
patterns easier to learn, compare, and use.
often concern space and time trade‐offs. They may address language and implementation issues as well.
Since reuse is often a factor in object‐oriented design, the consequences of a pattern include its impact
Pattern Name and Classification
on a system's flexibility, extensibility, or portability. Listing these consequences explicitly helps you The pattern's name conveys the essence of the pattern succinctly. A good name is vital, because it will
understand and evaluate them.
become part of your design vocabulary. The pattern's classification reflects the scheme we introduce in
Section 1.5.
Point of view affects one's interpretation of what is and isn't a pattern. One person's pattern can be
another person's primitive building block. For this book we have concentrated on patterns at a certain Intent
level of abstraction. Design patterns are not about designs such as linked lists and hash tables that can A short statement that answers the following questions: What does the design pattern do? What is its
be encoded in classes and reused as is. Nor are they complex, domain‐specific designs for an entire
rationale and intent? What particular design issue or problem does it address?
application or subsystem. The design patterns in this book are descriptions of communicating objects
and classes that are customized to solve a general design problem in a particular context. Also Known As
2. Other well‐known names for the pattern, if any. notations as we introduce them in the upcoming discussions. Finally, Appendix C contains source code
for the foundation classes we use in code samples.
Motivation
A scenario that illustrates a design problem and how the class and object structures in the pattern solve
Creational patterns
the problem. The scenario will help you understand the more abstract description of the pattern that
follows. Creational design patterns abstract the instantiation process. They help make a system independent of
how its objects are created, composed, and represented. A class creational pattern uses inheritance to
Applicability
vary the class that's instantiated, whereas an object creational pattern will delegate instantiation to
What are the situations in which the design pattern can be applied? What are examples of poor designs
another object.
that the pattern can address? How can you recognize these situations?
Creational patterns become important as systems evolve to depend more on object composition than
Structure
class inheritance. As that happens, emphasis shifts away from hard‐coding a fixed set of behaviors
A graphical representation of the classes in the pattern using a notation based on the Object Modeling
toward defining a smaller set of fundamental behaviors that can be composed into any number of more
Technique (OMT) [RBP+91]. We also use interaction diagrams [JCJO92, Boo94] to illustrate sequences of
complex ones. Thus creating objects with particular behaviors requires more than simply instantiating a
requests and collaborations between objects. Appendix B describes these notations in detail.
class.
Participants
There are two recurring themes in these patterns. First, they all encapsulate knowledge about which
The classes and/or objects participating in the design pattern and their responsibilities.
concrete classes the system uses. Second, they hide how instances of these classes are created and put
together. All the system at large knows about the objects is their interfaces as defined by abstract
Collaborations
classes. Consequently, the creational patterns give you a lot of flexibility in what gets created, who
How the participants collaborate to carry out their responsibilities.
creates it, how it gets created, and when. They let you configure a system with quot;productquot; objects that
Consequences vary widely in structure and functionality. Configuration can be static (that is, specified at compile‐time)
How does the pattern support its objectives? What are the trade‐offs and results of using the pattern? or dynamic (at run‐time).
What aspect of system structure does it let you vary independently?
Sometimes creational patterns are competitors. For example, there are cases when either Prototype
Implementation (117) or Abstract Factory (87) could be used profitably. At other times they are complementary: Builder
What pitfalls, hints, or techniques should you be aware of when implementing the pattern? Are there (97) can use one of the other patterns to implement which components get built. Prototype (117) can
language‐specific issues? use Singleton (127) in its implementation.
Sample Code Because the creational patterns are closely related, we'll study all five of them together to highlight their
Code fragments that illustrate how you might implement the pattern in C++ or Smalltalk. similarities and differences. We'll also use a common example—building a maze for a computer game—
to illustrate their implementations. The maze and the game will vary slightly from pattern to pattern.
Known Uses
Sometimes the game will be simply to find your way out of a maze; in that case the player will probably
Examples of the pattern found in real systems. We include at least two examples from different
only have a local view of the maze. Sometimes mazes contain problems to solve and dangers to
domains.
overcome, and these games may provide a map of the part of the maze that has been explored.
Related Patterns
We'll ignore many details of what can be in a maze and whether a maze game has a single or multiple
What design patterns are closely related to this one? What are the important differences? With which
players. Instead, we'll just focus on how mazes get created. We define a maze as a set of rooms. A room
other patterns should this one be used?
knows its neighbors; possible neighbors are another room, a wall, or a door to another room.
The appendices provide background information that will help you understand the patterns and the
The classes Room, Door, and Wall define the components of the maze used in all our examples. We
discussions surrounding them. Appendix A is a glossary of terminology we use. We've already
define only the parts of these classes that are important for creating a maze. We'll ignore players,
mentioned Appendix B, which presents the various notations. We'll also describe aspects of the
operations for displaying and wandering around in a maze, and other important functionality that isn't
relevant to building the maze.
3. virtual void Enter();
The following diagram shows the relationships between these classes:
private:
MapSite* _sides[4];
int _roomNumber;
};
The following classes represent the wall or door that occurs on each side of a room.
class Wall : public MapSite {
public:
Wall();
virtual void Enter();
};
class Door : public MapSite {
public:
Door(Room* = 0, Room* = 0);
virtual void Enter();
Each room has four sides. We use an enumeration Direction in C++ implementations to specify the Room* OtherSideFrom(Room*);
north, south, east, and west sides of a room:
private:
Room* _room1;
enum Direction {North, South, East, West};
Room* _room2;
The Smalltalk implementations use corresponding symbols to represent these directions.
bool _isOpen;
};
The class MapSite is the common abstract class for all the components of a maze. To simplify the
We need to know about more than just the parts of a maze. We'll also define a Maze class to represent
example, MapSite defines only one operation, Enter. Its meaning depends on what you're entering. If
a collection of rooms. Maze can also find a particular room given a room number using its RoomNo
you enter a room, then your location changes. If you try to enter a door, then one of two things happen:
operation.
If the door is open, you go into the next room. If the door is closed, then you hurt your nose.
class Maze {
class MapSite { public:
public: Maze();
virtual void Enter() = 0;
}; void AddRoom(Room*);
Enter provides a simple basis for more sophisticated game operations. For example, if you are in a room Room* RoomNo(int) const;
private:
and say quot;Go East,quot; the game can simply determine which MapSite is immediately to the east and then
// ...
call Enter on it. The subclass‐specific Enter operation will figure out whether your location changed or };
your nose got hurt. In a real game, Enter could take the player object that's moving about as an RoomNo could do a look‐up using a linear search, a hash table, or even a simple array. But we won't
argument. worry about such details here. Instead, we'll focus on how to specify the components of a maze object.
Room is the concrete subclass of MapSite that defines the key relationships between components in the Another class we define is MazeGame, which creates the maze. One straightforward way to create a
maze. It maintains references to other MapSite objects and stores a room number. The number will maze is with a series of operations that add components to a maze and then interconnect them. For
identify rooms in the maze. example, the following member function will create a maze consisting of two rooms with a door
between them:
class Room : public MapSite {
public:
Maze* MazeGame::CreateMaze () {
Room(int roomNo);
Maze* aMaze = new Maze;
Room* r1 = new Room(1);
MapSite* GetSide(Direction) const;
Room* r2 = new Room(2);
void SetSide(Direction, MapSite*);
Door* theDoor = new Door(r1, r2);
4. aMaze->AddRoom(r1); If CreateMaze is parameterized by various prototypical room, door, and wall objects, which it then
aMaze->AddRoom(r2);
copies and adds to the maze, then you can change the maze's composition by replacing these
prototypical objects with different ones. This is an example of the Prototype (117) pattern.
r1->SetSide(North, new Wall);
r1->SetSide(East, theDoor);
The remaining creational pattern, Singleton (127), can ensure there's only one maze per game and that
r1->SetSide(South, new Wall);
r1->SetSide(West, new Wall);
all game objects have ready access to it—without resorting to global variables or functions. Singleton
also makes it easy to extend or replace the maze without touching existing code.
r2->SetSide(North, new Wall);
r2->SetSide(East, new Wall);
r2->SetSide(South, new Wall);
Structural patterns
r2->SetSide(West, theDoor);
Structural patterns are concerned with how classes and objects are composed to form larger structures.
return aMaze;
} Structural class patterns use inheritance to compose interfaces or implementations. As a simple
This function is pretty complicated, considering that all it does is create a maze with two rooms. There
example, consider how multiple inheritance mixes two or more classes into one. The result is a class that
are obvious ways to make it simpler. For example, the Room constructor could initialize the sides with
combines the properties of its parent classes. This pattern is particularly useful for making
walls ahead of time. But that just moves the code somewhere else. The real problem with this member
independently developed class libraries work together. Another example is the class form of the Adapter
function isn't its size but its inflexibility. It hard‐codes the maze layout. Changing the layout means
(139) pattern. In general, an adapter makes one interface (the adaptee's) conform to another, thereby
changing this member function, either by overriding it—which means reimplementing the whole thing—
providing a uniform abstraction of different interfaces. A class adapter accomplishes this by inheriting
or by changing parts of it—which is error‐prone and doesn't promote reuse.
privately from an adaptee class. The adapter then expresses its interface in terms of the adaptee's.
The creational patterns show how to make this design more flexible, not necessarily smaller. In
Rather than composing interfaces or implementations, structural object patterns describe ways to
particular, they will make it easy to change the classes that define the components of a maze.
compose objects to realize new functionality. The added flexibility of object composition comes from
the ability to change the composition at run‐time, which is impossible with static class composition.
Suppose you wanted to reuse an existing maze layout for a new game containing (of all things)
enchanted mazes. The enchanted maze game has new kinds of components, like DoorNeedingSpell, a
Composite (163) is an example of a structural object pattern. It describes how to build a class hierarchy
door that can be locked and opened subsequently only with a spell; and EnchantedRoom, a room that
made up of classes for two kinds of objects: primitive and composite. The composite objects let you
can have unconventional items in it, like magic keys or spells. How can you change CreateMaze easily so
compose primitive and other composite objects into arbitrarily complex structures. In the Proxy (207)
that it creates mazes with these new classes of objects?
pattern, a proxy acts as a convenient surrogate or placeholder for another object. A proxy can be used in
many ways. It can act as a local representative for an object in a remote address space. It can represent
In this case, the biggest barrier to change lies in hard‐coding the classes that get instantiated. The
a large object that should be loaded on demand. It might protect access to a sensitive object. Proxies
creational patterns provide different ways to remove explicit references to concrete classes from code
provide a level of indirection to specific properties of objects. Hence they can restrict, enhance, or alter
that needs to instantiate them:
these properties.
If CreateMaze calls virtual functions instead of constructor calls to create the rooms, walls, and doors it
The Flyweight (195) pattern defines a structure for sharing objects. Objects are shared for at least two
requires, then you can change the classes that get instantiated by making a subclass of MazeGame and
reasons: efficiency and consistency. Flyweight focuses on sharing for space efficiency. Applications that
redefining those virtual functions. This approach is an example of the Factory Method (107) pattern.
use lots of objects must pay careful attention to the cost of each object. Substantial savings can be had
If CreateMaze is passed an object as a parameter to use to create rooms, walls, and doors, then you by sharing objects instead of replicating them. But objects can be shared only if they don't define
can change the classes of rooms, walls, and doors by passing a different parameter. This is an example context‐dependent state. Flyweight objects have no such state. Any additional information they need to
of the Abstract Factory (87) pattern. perform their task is passed to them when needed. With no context‐dependent state, Flyweight objects
may be shared freely.
If CreateMaze is passed an object that can create a new maze in its entirety using operations for adding
rooms, doors, and walls to the maze it builds, then you can use inheritance to change parts of the maze Whereas Flyweight shows how to make lots of little objects, Facade (185) shows how to make a single
or the way the maze is built. This is an example of the Builder (97) pattern. object represent an entire subsystem. A facade is a representative for a set of objects. The facade
carries out its responsibilities by forwarding messages to the objects it represents. The Bridge (151)
5. pattern separates an object's abstraction from its implementation so that you can vary them Other behavioral object patterns are concerned with encapsulating behavior in an object and delegating
independently. requests to it. The Strategy (315) pattern encapsulates an algorithm in an object. Strategy makes it easy
to specify and change the algorithm an object uses. The Command (233) pattern encapsulates a request
Decorator (175) describes how to add responsibilities to objects dynamically. Decorator is a structural in an object so that it can be passed as a parameter, stored on a history list, or manipulated in other
pattern that composes objects recursively to allow an open‐ended number of additional responsibilities. ways. The State (305) pattern encapsulates the states of an object so that the object can change its
For example, a Decorator object containing a user interface component can add a decoration like a behavior when its state object changes. Visitor (331) encapsulates behavior that would otherwise be
border or shadow to the component, or it can add functionality like scrolling and zooming. We can add distributed across classes, and Iterator (257) abstracts the way you access and traverse objects in an
two decorations simply by nesting one Decorator object within another, and so on for additional
aggregate.
decorations. To accomplish this, each Decorator object must conform to the interface of its component
and must forward messages to it. The Decorator can do its job (such as drawing a border around the
component) either before or after forwarding a message.
Behavioral patterns
Behavioral patterns are concerned with algorithms and the assignment of responsibilities between
objects. Behavioral patterns describe not just patterns of objects or classes but also the patterns of
communication between them. These patterns characterize complex control flow that's difficult to
follow at run‐time. They shift your focus away from flow of control to let you concentrate just on the
way objects are interconnected.
Behavioral class patterns use inheritance to distribute behavior between classes. This chapter includes
two such patterns. Template Method (325) is the simpler and more common of the two. A template
method is an abstract definition of an algorithm. It defines the algorithm step by step. Each step invokes
either an abstract operation or a primitive operation. A subclass fleshes out the algorithm by defining
the abstract operations. The other behavioral class pattern is Interpreter (243), which represents a
grammar as a class hierarchy and implements an interpreter as an operation on instances of these
classes.
Behavioral object patterns use object composition rather than inheritance. Some describe how a group
of peer objects cooperate to perform a task that no single object can carry out by itself. An important
issue here is how peer objects know about each other. Peers could maintain explicit references to each
other, but that would increase their coupling. In the extreme, every object would know about every
other. The Mediator (273) pattern avoids this by introducing a mediator object between peers. The
mediator provides the indirection needed for loose coupling.
Chain of Responsibility (223) provides even looser coupling. It lets you send requests to an object
implicitly through a chain of candidate objects. Any candidate may fulfill the request depending on run‐
time conditions. The number of candidates is open‐ended, and you can select which candidates
participate in the chain at run‐time.
The Observer (293) pattern defines and maintains a dependency between objects. The classic example
of Observer is in Smalltalk Model/View/Controller, where all views of the model are notified whenever
the model's state changes.
6. visitor, it sends a request to the visitor that encodes the element's class. It also includes the element as
Visitor pattern an argument. The visitor will then execute the operation for that element—the operation that used to
be in the class of the element.
Intent
For example, a compiler that didn't use visitors might type‐check a procedure by calling the TypeCheck
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a
operation on its abstract syntax tree. Each of the nodes would implement TypeCheck by calling
new operation without changing the classes of the elements on which it operates.
TypeCheck on its components (see the preceding class diagram). If the compiler type‐checked a
Motivation procedure using visitors, then it would create a TypeCheckingVisitor object and call the Accept operation
on the abstract syntax tree with that object as an argument. Each of the nodes would implement Accept
Consider a compiler that represents programs as abstract syntax trees. It will need to perform
by calling back on the visitor: an assignment node calls VisitAssignment operation on the visitor, while a
operations on abstract syntax trees for quot;static semanticquot; analyses like checking that all variables are
variable reference calls VisitVariableReference. What used to be the TypeCheck operation in class
defined. It will also need to generate code. So it might define operations for type‐checking, code
AssignmentNode is now the VisitAssignment operation on TypeCheckingVisitor.
optimization, flow analysis, checking for variables being assigned values before they're used, and so on.
Moreover, we could use the abstract syntax trees for pretty‐printing, program restructuring, code
To make visitors work for more than just type‐checking, we need an abstract parent class NodeVisitor
instrumentation, and computing various metrics of a program.
for all visitors of an abstract syntax tree. NodeVisitor must declare an operation for each node class. An
application that needs to compute program metrics will define new subclasses of NodeVisitor and will
Most of these operations will need to treat nodes that represent assignment statements differently
no longer need to add application‐specific code to the node classes. The Visitor pattern encapsulates the
from nodes that represent variables or arithmetic expressions. Hence there will be one class for
operations for each compilation phase in a Visitor associated with that phase.
assignment statements, another for variable accesses, another for arithmetic expressions, and so on.
The set of node classes depends on the language being compiled, of course, but it doesn't change much
for a given language.
This diagram shows part of the Node class hierarchy. The problem here is that distributing all these
operations across the various node classes leads to a system that's hard to understand, maintain, and
change. It will be confusing to have type‐checking code mixed with pretty‐printing code or flow analysis
code. Moreover, adding a new operation usually requires recompiling all of these classes. It would be
better if each new operation could be added separately, and the node classes were independent of the
operations that apply to them. With the Visitor pattern, you define two class hierarchies: one for the elements being operated on (the
Node hierarchy) and one for the visitors that define operations on the elements (the NodeVisitor
We can have both by packaging related operations from each class in a separate object, called a visitor,
hierarchy). You create a new operation by adding a new subclass to the visitor class hierarchy. As long as
and passing it to elements of the abstract syntax tree as it's traversed. When an element quot;acceptsquot; the
7. the grammar that the compiler accepts doesn't change (that is, we don't have to add new Node declares a Visit operation for each class of ConcreteElement in the object structure. The operation's
subclasses), we can add new functionality simply by defining new NodeVisitor subclasses. name and signature identifies the class that sends the Visit request to the visitor. That lets the visitor
determine the concrete class of the element being visited. Then the visitor can access the element
Applicability directly through its particular interface.
Use the Visitor pattern when an object structure contains many classes of objects with differing
ConcreteVisitor (TypeCheckingVisitor)
interfaces, and you want to perform operations on these objects that depend on their concrete classes.
implements each operation declared by Visitor. Each operation implements a fragment of the algorithm
many distinct and unrelated operations need to be performed on objects in an object structure, and you defined for the corresponding class of object in the structure. ConcreteVisitor provides the context for
want to avoid quot;pollutingquot; their classes with these operations. Visitor lets you keep related operations the algorithm and stores its local state. This state often accumulates results during the traversal of the
together by defining them in one class. When the object structure is shared by many applications, use structure.
Visitor to put operations in just those applications that need them.
Element (Node)
the classes defining the object structure rarely change, but you often want to define new operations defines an Accept operation that takes a visitor as an argument.
over the structure. Changing the object structure classes requires redefining the interface to all visitors,
ConcreteElement (AssignmentNode,VariableRefNode)
which is potentially costly. If the object structure classes change often, then it's probably better to
• implements an Accept operation that takes a visitor as an argument.
define the operations in those classes.
• ObjectStructure (Program)
Structure • can enumerate its elements.
• may provide a high‐level interface to allow the visitor to visit its elements.
• may either be a composite (see Composite (163)) or a collection such as a list or a set.
Collaborations
A client that uses the Visitor pattern must create a ConcreteVisitor object and then traverse the object
structure, visiting each element with the visitor.
When an element is visited, it calls the Visitor operation that corresponds to its class. The element
supplies itself as an argument to this operation to let the visitor access its state, if necessary.
The following interaction diagram illustrates the collaborations between an object structure, a visitor,
and two elements:
Participants
Visitor (NodeVisitor)
8. Consequences MyType and YourType do not have to be related through inheritance at all.
Some of the benefits and liabilities of the Visitor pattern are as follows:
Accumulating state. Visitors can accumulate state as they visit each element in the object structure.
Without a visitor, this state would be passed as extra arguments to the operations that perform the
Visitor makes adding new operations easy. Visitors make it easy to add operations that depend on the
traversal, or they might appear as global variables.
components of complex objects. You can define a new operation over an object structure simply by
adding a new visitor. In contrast, if you spread functionality over many classes, then you must change
Breaking encapsulation. Visitor's approach assumes that the ConcreteElement interface is powerful
each class to define a new operation.
enough to let visitors do their job. As a result, the pattern often forces you to provide public operations
that access an element's internal state, which may compromise its encapsulation.
A visitor gathers related operations and separates unrelated ones. Related behavior isn't spread over the
classes defining the object structure; it's localized in a visitor. Unrelated sets of behavior are partitioned
Implementation
in their own visitor subclasses. That simplifies both the classes defining the elements and the algorithms
Each object structure will have an associated Visitor class. This abstract visitor class declares a
defined in the visitors. Any algorithm‐specific data structures can be hidden in the visitor.
VisitConcreteElement operation for each class of ConcreteElement defining the object structure. Each
Adding new ConcreteElement classes is hard. The Visitor pattern makes it hard to add new subclasses of Visit operation on the Visitor declares its argument to be a particular ConcreteElement, allowing the
Element. Each new ConcreteElement gives rise to a new abstract operation on Visitor and a Visitor to access the interface of the ConcreteElement directly. ConcreteVisitor classes override each
corresponding implementation in every ConcreteVisitor class. Sometimes a default implementation can Visit operation to implement visitor‐specific behavior for the corresponding ConcreteElement class.
be provided in Visitor that can be inherited by most of the ConcreteVisitors, but this is the exception
The Visitor class would be declared like this in C++:
rather than the rule.
class Visitor {
So the key consideration in applying the Visitor pattern is whether you are mostly likely to change the public:
algorithm applied over an object structure or the classes of objects that make up the structure. The virtual void VisitElementA(ElementA*);
virtual void VisitElementB(ElementB*);
Visitor class hierarchy can be difficult to maintain when new ConcreteElement classes are added
frequently. In such cases, it's probably easier just to define operations on the classes that make up the // and so on for other concrete elements
structure. If the Element class hierarchy is stable, but you are continually adding operations or changing protected:
Visitor();
algorithms, then the Visitor pattern will help you manage the changes.
};
Each class of ConcreteElement implements an Accept operation that calls the matching Visit... operation
Visiting across class hierarchies. An iterator (see Iterator (257)) can visit the objects in a structure as it
on the visitor for that ConcreteElement. Thus the operation that ends up getting called depends on both
traverses them by calling their operations. But an iterator can't work across object structures with
the class of the element and the class of the visitor.10
different types of elements. For example, the Iterator interface defined on page 263 can access only
objects of type Item: The concrete elements are declared as
1. template <class Item> class Element {
2. class Iterator { public:
3. // ... virtual ~Element();
4. Item CurrentItem() const; virtual void Accept(Visitor&) = 0;
5. }; protected:
This implies that all elements the iterator can visit have a common parent class Item. Element();
};
Visitor does not have this restriction. It can visit objects that don't have a common parent class. You can
class ElementA : public Element {
add any type of object to a Visitor interface. For example, in
public:
ElementA();
class Visitor {
virtual void Accept(Visitor& v) { v.VisitElementA(this); }
public:
};
// ...
void VisitMyType(MyType*);
class ElementB : public Element {
void VisitYourType(YourType*);
public:
};
ElementB();
9. virtual void Accept(Visitor& v) { v.VisitElementB(this); } Often the object structure is responsible for iteration. A collection will simply iterate over its elements,
};
calling the Accept operation on each. A composite will commonly traverse itself by having each Accept
A CompositeElement class might implement Accept like this:
operation traverse the element's children and call Accept on each of them recursively.
class CompositeElement : public Element {
Another solution is to use an iterator to visit the elements. In C++, you could use either an internal or
public:
virtual void Accept(Visitor&); external iterator, depending on what is available and what is most efficient. In Smalltalk, you usually use
private:
an internal iterator using do: and a block. Since internal iterators are implemented by the object
List<Element*>* _children;
structure, using an internal iterator is a lot like making the object structure responsible for iteration. The
};
main difference is that an internal iterator will not cause double‐dispatching—it will call an operation on
void CompositeElement::Accept (Visitor& v) {
the visitor with an element as an argument as opposed to calling an operation on the element with the
ListIterator<Element*> i(_children);
visitor as an argument. But it's easy to use the Visitor pattern with an internal iterator if the operation
for (i.First(); !i.IsDone(); i.Next()) { on the visitor simply calls the operation on the element without recursing.
i.CurrentItem()->Accept(v);
}
You could even put the traversal algorithm in the visitor, although you'll end up duplicating the traversal
v.VisitCompositeElement(this);
code in each ConcreteVisitor for each aggregate ConcreteElement. The main reason to put the traversal
}
Here are two other implementation issues that arise when you apply the Visitor pattern: strategy in the visitor is to implement a particularly complex traversal, one that depends on the results
of the operations on the object structure. We'll give an example of such a case in the Sample Code.
Double dispatch. Effectively, the Visitor pattern lets you add operations to classes without changing
them. Visitor achieves this by using a technique called double‐dispatch. It's a well‐known technique. In Sample Code
fact, some programming languages support it directly (CLOS, for example). Languages like C++ and Because visitors are usually associated with composites, we'll use the Equipment classes defined in the
Smalltalk support single‐dispatch. Sample Code of Composite (163) to illustrate the Visitor pattern. We will use Visitor to define operations
for computing the inventory of materials and the total cost for a piece of equipment. The Equipment
In single‐dispatch languages, two criteria determine which operation will fulfill a request: the name of
classes are so simple that using Visitor isn't really necessary, but they make it easy to see what's
the request and the type of receiver. For example, the operation that a GenerateCode request will call
involved in implementing the pattern.
depends on the type of node object you ask. In C++, calling GenerateCode on an instance of
VariableRefNode will call VariableRefNode::GenerateCode (which generates code for a variable Here again is the Equipment class from Composite (163). We've augmented it with an Accept operation
reference). Calling GenerateCode on an AssignmentNode will call AssignmentNode::GenerateCode to let it work with a visitor.
(which will generate code for an assignment). The operation that gets executed depends both on the
class Equipment {
kind of request and the type of the receiver.
public:
virtual ~Equipment();
quot;Double‐dispatchquot; simply means the operation that gets executed depends on the kind of request and
the types of two receivers. Accept is a double‐dispatch operation. Its meaning depends on two types: const char* Name() { return _name; }
the Visitor's and the Element's. Double‐dispatching lets visitors request different operations on each
virtual Watt Power();
class of element.11 virtual Currency NetPrice();
virtual Currency DiscountPrice();
This is the key to the Visitor pattern: The operation that gets executed depends on both the type of
virtual void Accept(EquipmentVisitor&);
Visitor and the type of Element it visits. Instead of binding operations statically into the Element
protected:
interface, you can consolidate the operations in a Visitor and use Accept to do the binding at run‐time. Equipment(const char*);
Extending the Element interface amounts to defining one new Visitor subclass rather than many new private:
const char* _name;
Element subclasses.
};
The Equipment operations return the attributes of a piece of equipment, such as its power consumption
Who is responsible for traversing the object structure? A visitor must visit each element of the object
and cost. Subclasses redefine these operations appropriately for specific types of equipment (e.g., a
structure. The question is, how does it get there? We can put responsibility for traversal in any of three
chassis, drives, and planar boards).
places: in the object structure, in the visitor, or in a separate iterator object (see Iterator (257)).
10. Currency _total;
The abstract class for all visitors of equipment has a virtual function for each subclass of equipment, as
};
shown next. All of the virtual functions do nothing by default.
void PricingVisitor::VisitFloppyDisk (FloppyDisk* e) {
class EquipmentVisitor { _total += e->NetPrice();
public: }
virtual ~EquipmentVisitor();
void PricingVisitor::VisitChassis (Chassis* e) {
virtual void VisitFloppyDisk(FloppyDisk*); _total += e->DiscountPrice();
virtual void VisitCard(Card*); }
virtual void VisitChassis(Chassis*);
PricingVisitor will compute the total cost of all nodes in the equipment structure. Note that
virtual void VisitBus(Bus*);
PricingVisitor chooses the appropriate pricing policy for a class of equipment by dispatching to the
// and so on for other concrete subclasses of Equipment corresponding member function. What's more, we can change the pricing policy of an equipment
protected:
structure just by changing the PricingVisitor class.
EquipmentVisitor();
};
We can define a visitor for computing inventory like this:
Equipment subclasses define Accept in basically the same way: It calls the EquipmentVisitor operation
that corresponds to the class that received the Accept request, like this: class InventoryVisitor : public EquipmentVisitor {
public:
void FloppyDisk::Accept (EquipmentVisitor& visitor) { InventoryVisitor();
visitor.VisitFloppyDisk(this);
} Inventory& GetInventory();
Equipment that contains other equipment (in particular, subclasses of CompositeEquipment in the
Composite pattern) implements Accept by iterating over its children and calling Accept on each of them. virtual void VisitFloppyDisk(FloppyDisk*);
virtual void VisitCard(Card*);
Then it calls the Visit operation as usual. For example, Chassis::Accept could traverse all the parts in the virtual void VisitChassis(Chassis*);
chassis as follows: virtual void VisitBus(Bus*);
// ...
void Chassis::Accept (EquipmentVisitor& visitor) {
for ( private:
ListIterator i(_parts); Inventory _inventory;
!i.IsDone(); };
i.Next() The InventoryVisitor accumulates the totals for each type of equipment in the object structure.
){
InventoryVisitor uses an Inventory class that defines an interface for adding equipment (which we won't
i.CurrentItem()->Accept(visitor);
bother defining here).
}
visitor.VisitChassis(this);
} void InventoryVisitor::VisitFloppyDisk (FloppyDisk* e) {
_inventory.Accumulate(e);
Subclasses of EquipmentVisitor define particular algorithms over the equipment structure. The
}
PricingVisitor computes the cost of the equipment structure. It computes the net price of all simple
equipment (e.g., floppies) and the discount price of all composite equipment (e.g., chassis and buses). void InventoryVisitor::VisitChassis (Chassis* e) {
_inventory.Accumulate(e);
class PricingVisitor : public EquipmentVisitor { }
public: Here's how we can use an InventoryVisitor on an equipment structure:
PricingVisitor();
Equipment* component;
Currency& GetTotalPrice(); InventoryVisitor visitor;
virtual void VisitFloppyDisk(FloppyDisk*); component->Accept(visitor);
virtual void VisitCard(Card*); cout << quot;Inventory quot;
virtual void VisitChassis(Chassis*); << component->Name()
virtual void VisitBus(Bus*); << visitor.GetInventory();
// ...
private:
11. [:stream | tStream := stream copy.
Now we'll show how to implement the Smalltalk example from the Interpreter pattern (see page 248)
(tStream nextAvailable:
with the Visitor pattern. Like the previous example, this one is so small that Visitor probably won't buy literalExp components size
us much, but it provides a good illustration of how to use the pattern. Further, it illustrates a situation in ) = literalExp components
ifTrue: [finalState add: tStream]
which iteration is the visitor's responsibility.
].
^ finalState
The object structure (regular expressions) is made of four classes, and all of them have an accept:
Known Uses
method that takes the visitor as an argument. In class SequenceExpression, the accept: method is
The Smalltalk‐80 compiler has a Visitor class called ProgramNodeEnumerator. It's used primarily for
accept: aVisitor
algorithms that analyze source code. It isn't used for code generation or pretty‐printing, although it
^ aVisitor visitSequence: self
In class RepeatExpression, the accept: method sends the visitRepeat: message. In class could be.
AlternationExpression, it sends the visitAlternation: message. In class LiteralExpression, it sends the
IRIS Inventor [Str93] is a toolkit for developing 3‐D graphics applications. Inventor represents a three‐
visitLiteral: message.
dimensional scene as a hierarchy of nodes, each representing either a geometric object or an attribute
The four classes also must have accessing functions that the visitor can use. For SequenceExpression of one. Operations like rendering a scene or mapping an input event require traversing this hierarchy in
these are expression1 and expression2; for AlternationExpression these are alternative1 and different ways. Inventor does this using visitors called quot;actions.quot; There are different visitors for
alternative2; for RepeatExpression it is repetition; and for LiteralExpression these are components. rendering, event handling, searching, filing, and determining bounding boxes.
The ConcreteVisitor class is REMatchingVisitor. It is responsible for the traversal because its traversal To make adding new nodes easier, Inventor implements a double‐dispatch scheme for C++. The scheme
algorithm is irregular. The biggest irregularity is that a RepeatExpression will repeatedly traverse its relies on run‐time type information and a two‐dimensional table in which rows represent visitors and
component. The class REMatchingVisitor has an instance variable inputState. Its methods are essentially columns represent node classes. The cells store a pointer to the function bound to the visitor and node
the same as the match: methods of the expression classes in the Interpreter pattern except they replace class.
the argument named inputState with the expression node being matched. However, they still return the
Mark Linton coined the term quot;Visitorquot; in the X Consortium's Fresco Application Toolkit specification
set of streams that the expression would match to identify the current state.
[LP93].
visitSequence: sequenceExp
Related Patterns
inputState := sequenceExp expression1 accept: self.
^ sequenceExp expression2 accept: self.
Composite (163): Visitors can be used to apply an operation over an object structure defined by the
visitRepeat: repeatExp Composite pattern.
| finalState |
finalState := inputState copy. Interpreter (243): Visitor may be applied to do the interpretation.
[inputState isEmpty]
whileFalse:
[inputState := repeatExp repetition accept: self.
finalState addAll: inputState].
^ finalState
visitAlternation: alternateExp
| finalState originalState |
originalState := inputState.
finalState := alternateExp alternative1 accept: self.
inputState := originalState.
finalState addAll: (alternateExp alternative2 accept: self).
^ finalState
visitLiteral: literalExp
| finalState tStream |
finalState := Set new.
inputState
do: