s an architect working for my own company and the president of the Greater Lansing area .NET Users Group (glugnet.org
) I'm constantly looking for customers and trying to increase technology awareness in the greater community. In essence, like every business person I am always looking for customers. A natural extension of my consulting business is matching employees to employers. As a result I began working on Motown-Jobs, a Web site where job seekers can post resumes and search for jobs posted by job owners (employers). When writing down the use cases for Motown-Jobs, I knew that I was very familiar with some aspects of matching employers to employees as I had been doing it for years. I was also sure that I might miss ways to capitalize on business opportunities because of expertise I didn’t have. To deal with my limited expertise and anticipate future revisions I hedged when I designed Motown-Jobs. Let me explain.
I want Motown-Jobs to permit employers to post jobs for a fee and employees to post resumes and search for jobs for free. I also suspect employers may want to know about the traffic their job listings generate, and future advertisers might be interested in general site traffic. In fact, site statistics of all kinds might be useful in unanticipated ways. When you can anticipate that a system may require changes but can't define exactly what those changes will be, the challenge becomes one of programming defensively.
In this context, defensive programming means that you may want to add future behavior but without changing core classes constantly. For example, if a Job class is central to the system, then repeatedly updating the Job class would be disruptive. To respond to this potentiality defensively and proactively you can employ the Visitor behavior pattern.
Think of the Visitor pattern as you would a census taker approaching a household. The census taker shows up, gathers some information, and goes away. (The going away part is good.) You could just as easily substitute the census taker with a garbage man, maid, repairman, or a favorite rich aunt who cleans all the time. The point is that the nature of a householdthe number of walls, rooms, furniture, and regular occupantsdoesn't change that much; the visitor can drop in, do what that particular kind of visitor doestake census information, clean your house, or leave cashand go away.
Interestingly enough, this metaphor holds up nicely because a visitor can be anything and do anything without changing the nature of the host. In this metaphor, the household plays host to any number and variety of visitors. Enough said.
Defining Hosts and Visitors
The Motown-Jobs code defines important classes for job listings, resumes, and advertisements. Each of these classes contains information about their contents. Job listing classes, for example, might contain information about the number of times they were viewed, when they were posted, and their individual expiration dates. You can define classes such as job listing and advertisement to have a relative dollar value. For job listing and advertisement you may want to determine the value of the time remaining for each listing. Finally, the future may hold new, useful, and valuable ways to use the data represented by instances of job listing, advertisement, and resume. From the perspective of the visitor pattern these classes represent hosts. In the future you may want to define new ways to drop in (visit) and collect information about instances of these classes.
The point is that by defining a facet of job listing, resume, and advertisement generically as hosts you can later define specific visitor classes to drop in and perform operations on instances of these classes.
Technically, implementing the Visitor pattern is quite straightforward. You define an abstract class or interface named Hostor IHost for interfacesthat has a single method named Accept
that accepts a Visitor abstract class or an interface. Each Visitor contains methods called Visit
for each kind of Host. You call the Host's Accept
method, passing in the Visitor, and the Host's Accept
method calls the Visitor.Visit
method for that kind of Host. You code the Visit
method for each Host to glean the information or perform the extended operation on the Host. The important point is that after implementing the Accept
method you don't need to change the hosts to add new behavior.
This kind of method dispatching is called double dispatching
. You use double dispatching because hosts know only that visitors will be visiting and visitors know only that the host will ask the visitor to stay and visit. This interchange looks loosely like:
//Host: Hello, come on in and visit for a spell
Subsequently, the visitor runs around poking at all your stuff and asking questions.
If all these folksy analogies aren't helping, perhaps the UML class diagram for the Visitor pattern will clear it up for you. I encourage you to pick up a copy of the Gang of Four book, Design Patterns
, published by Addison Wesley.