2012-03-21

Refactoring and the Visitor pattern 1.

I like the Visitor pattern as one tool for embracing the Open-Closed Principle.
In one of my clients' project I faced two situations where it almost prevented refactoring and since I think the two cases are interesting and important enough I'll demonstrate them in two blog posts.

My roles in the project were development lead, architect and coach and with these responsibilities I tried to improve both, the system's structure and architecture as much as possible. One structural issue was that all subtypes of a generic and central abstract type implementing the blackboard architectural pattern were put in the same module. This lead to a confusing and complicated hierarchy consisting of more than 100 packages and more than 1.000 classes.
I didn't want to let that module grow further for obvious reasons, so I decided to create two new modules: One for the new subtype and another one for components and classes commonly used by the old and the new module. This is why I have extracted the blackboard base type into the commons module and had to deal with the Visitor pattern.
The code was similar to the following:
public abstract class BlackBoardBaseType {
  ...
  public abstract void accept(BlackBoardVisitor visitor);
}

public interface BlackBoardVisitor {

  visit(BlackBoardSubType1 blackBoardSubType);
  ...
  visit(BlackBoardSubTypeN blackBoardSubType);
}

public class BlackBoardSubType1 extends BlackBoardBaseType {
  ...
  public void accept(BlackBoardVisitor visitor) {
    visitor.visit(this);
  }
}

public class BlackBoardSubTypeN extends BlackBoardBaseType {
  ...
  public void accept(BlackBoardVisitor visitor) {
    visitor.visit(this);
  }
}

So if I just would have taken the base type without modification, then I should have extracted all its subtypes, too, because of the visitor. Therefore an extra step was necessary, namely the abstraction of the visitor from the concrete subtypes, what I have solved by introducing an empty (in other uses marker) interface.
public abstract class BlackBoardBaseType {
  ...
  public abstract void accept(EmptyBlackBoardVisitor visitor);
}

public interface EmptyBlackBoardVisitor {
}

public interface BlackBoardVisitor extends EmptyBlackBoardVisitor {

  visit(BlackBoardSubType1 blackBoardSubType);
  ...
  visit(BlackBoardSubTypeN blackBoardSubType);
}

public class BlackBoardSubType1 extends BlackBoardBaseType {
  ...
  public void accept(EmptyBlackBoardVisitor visitor) {
    visitor.visit(this);
  }
}

public class BlackBoardSubTypeN extends BlackBoardBaseType {
  ...
  public void accept(EmptyBlackBoardVisitor visitor) {
    visitor.visit(this);
  }
}

By doing so there was no indirect dependency of the base type on the sub types via the visitor anymore and I could extract it and also implement the new subtype.

No comments:

Post a Comment