Monday, 8 October 2007

Visitor Pattern in Java

Current Problem Without Visitor Pattern(As Described in Robert C. Martin in "The Visitor Family of Design Patterns") book :-

For example, suppose you have a hierarchy of Modem objects. The base class has the generic methods common to all modems. The derivatives represent the drivers for many different modem manufacturers and types. Suppose also that you have a requirement to add a new method, named configureForUnix, to the
hierarchy. This method will configure the modem to work with the UNIX operating system.

It will do something different in each modem derivative, because each different
modem has its own particular idiosyncrasies for setting its configuration, and dealing with UNIX.Unfortunately adding configureForUnix begs a terrible set of questions. What aboutWindows, what about MacOs, what about Linux? Must we really add a new method to the Modem hierarchy for every new operating system that we use? Clearly this is ugly.

We’ll never be able to close the Modem interface. Every time a new operating system
comes along we’ll have to change that interface and redeploy all the modem software.

Visitor Pattern:-
So we need to have a pattern that separates an algorithm from an object structure.Benefit of this separation is the ability to add new operations to existing object structures without modifying those structures.

The idea is to use a structure of element classes, each of which has an accept method that takes a visitor object as an argument. Visitor is an interface that has a visit() method for each element class. The accept() method of an element class calls back the visit() method for its class. Separate concrete visitor classes can then be written that perform some particular operations.

One of these visit() methods of a concrete visitor can be thought of as methods not of a single class, but rather methods of a pair of classes: the concrete visitor and the particular element class. Thus the visitor pattern simulates double dispatch in a conventional single-dispatch object-oriented language such as Java, Smalltalk, and C++.

The visitor pattern also specifies how iteration occurs over the object structure. In the simplest version, where each algorithm needs to iterate in the same way, the accept() method of a container element, in addition to calling back the visit() method of the visitor, also passes the visitor object to the accept() method of all its constituent child elements.

Example of a Visitor Pattern:

interface Visitor {
void visit(Kitchen kitchen);
void visit(MainHall mainHall);
void visit(BedRoom bedRoom);
int visit(NewHouse newHouse);
}

interface Visitable {
void accept(Visitor visitor);
}

class NewHouseVisitor implements Visitor {

int totalCost=0;

public int getTotalCost() {
return totalCost;
}

public void setTotalCost(int totalCost) {
this.totalCost = totalCost;
}

public void visit(Kitchen kitchen) {
int cost=kitchen.getCost();
totalCost+=cost;
}

public void visit(MainHall mainHall) {
int cost=mainHall.getCost();
totalCost+=cost;
}

public void visit(BedRoom bedRoom) {
int cost=bedRoom.getCost();
totalCost+=cost;
}

public int visit(NewHouse newHouse) {
int otherAdditionalCosts=1000;
totalCost+=otherAdditionalCosts;
return totalCost;
      }

}

class BedRoom implements Visitable {

private int cost;

public int getCost() {
return cost;
}

public void setCost(int cost) {
this.cost = cost;
}

public void accept(Visitor kitchenVisitor) {
kitchenVisitor.visit(this);
   }
}

.... Similarly provide same implementation for Kitchen class,MainHall class as of BedRoom class.

class NewHouse implements Visitable {

private Kitchen kitchen=new Kitchen();
private MainHall mainHall=new MainHall();
private BedRoom bedRoom=new BedRoom();


public BedRoom getBedRoom() {
return bedRoom;
}


public void setBedRoom(BedRoom bedRoom) {
this.bedRoom = bedRoom;
}


public Kitchen getKitchen() {
return kitchen;
}


public void setKitchen(Kitchen kitchen) {
this.kitchen = kitchen;
}


public MainHall getMainHall() {
return mainHall;
}


public void setMainHall(MainHall mainHall) {
this.mainHall = mainHall;
}

public void accept(Visitor newHouseVisitor) {
newHouseVisitor.visit(this);
newHouseVisitor.visit(kitchen);
newHouseVisitor.visit(mainHall);
newHouseVisitor.visit(bedRoom);
   }
}

Finally provide the main method and calculate the total cost.

public class VisitorDemo {

public static void main(String[] args) {

NewHouseVisitor newHouseVisitor=new NewHouseVisitor();

Kitchen kitchen=new Kitchen();
kitchen.setCost(450);

MainHall mainHall=new MainHall();
mainHall.setCost(900);

BedRoom bedRoom=new BedRoom();
bedRoom.setCost(400);

NewHouse newHouse=new NewHouse();
newHouse.setBedRoom(bedRoom);
newHouse.setKitchen(kitchen);
newHouse.setMainHall(mainHall);

newHouse.accept(newHouseVisitor);
System.out.println("Total Cost Of new House:" + newHouseVisitor.getTotalCost());
    }
}

Prints: Total Cost Of new House:2750

Hope this explanation helps if unknown.

8 comments:

Anonymous said...

You should format your code better.

Anonymous said...

There have been requests for better code formatting many times over the past posts on this blog, but apparently they have all been ignored...

*sigh*

Alex Miller said...

You might be interested in my article on visitors at http://tech.puredanger.com/2007/07/16/visitor/ which goes into a bit more depth.

Alex Miller said...

Sorry, that was Patterns I Love/Hate #3: Visitor.

JP said...

Alex,

Thanks Alex.I love your blog and especially your Java7 updates.

Sorry once again i will really make some more spare time and make formatting better in later posts.

Thanks
Prashant

Anonymous said...

There are plenty of code formatting options, but if you really can't be bothered, this is an easy way of doing it!
http://www.palfrader.org/code2html/code2html.html

JP said...

Hi,

Thanks a lot for your suggestions.Will surely use the tool you have suggested.Even i started downloading it. :-)

Anonymous said...

You start with a problem example, i.e. modem and when it comes to the implementation you use something completely different, i.e. houses. Makes it quite hard to follow...