Null Object And Visitor

While writing the SceneBeans animation toolkit, I've just discovered a neat fit between the NullObject pattern and the VisitorPattern. For example, taking the following class hierarchy, and visitor interface:

interface Visitor {
void visitRectangle( Rectangle r );
void visitCircle( Circle c );
void visitPolygon( Polygon p );
void visitCompositeShape( Composite_Shape cs );
void visitTransformation( Transformation t );
}

abstract class Shape { void draw( Graphics2D g2d ); void accept( Visitor v ); }

class Rectangle extends Shape { public void draw( Graphics2D g ) { ... draw rectangle onto g ... } public void accept( Visitor v ) { v.visitRectangle(this); } ... } ... other shapes are implemented the same way ...

One can implement a NullObject like this:

class Null_Shape extends Shape {
public void draw( Graphics2D g ) { /* Don't draw anything */ }
public void accept( Visitor v ) { /* Don't double-dispatch */ }
....
}

The visitor does not even have to know about the use of the NullObject pattern! Therefore, concrete visitors do not need to handle special cases that might be caused by the presence of NullObject shapes in the scene graph, such as empty areas or zero-length paths.

I find this rather elegant. However, have I missed any drawbacks to this way of combining the two patterns?

--NatPryce

Looks fine to me. If you had a "visitNullShape" function in your Visitor interface, would any of the Visitor subclases provide a meaningful implementation for it? (I didn't think so -- so what you did must be right. ;-) -- JeffGrigg


I have recently discovered a drawback with this design. However, the drawback was not caused by the combination of NullObject and VisitorPattern, but in a refactoring that caused NullObjects to represent concepts in the system, and so no longer be true NullObjects. See NullObjectAndRefactoring for details. --NatPryce


This is just an optimisation, right? What harm would it do if null objects double-dispatched as normal? At worst, there would be a visitNullShape method that nobody overrode.

Suppose we want a visitor that counts all the nodes. Does "all" include null objects? Would it be clearest if we overrode visitNullShape and then either counted, or had an empty implementation (perhaps with a comment) to show we had at least thought about it? --

I don't think it is an optimisation -- in any case, I never originally used it for that reason. My thinking was: "How do you double-dispatch to a null reference? You can't. Therefore you shouldn't double-dispatch to a NullObject either." Also, I found the fit between the two patterns particularly aesthetically pleasing! --NatPryce

I agree visitor goes well with null object. Null object is about replacing tests against null with polymorphism. Usually with polymorphism, single-dispatch is enough, but sometimes you need double-dispatch, and visitor is a general way to achieve that. This line of reasoning leads me to think it is natural to double-dispatch on null objects. It's as natural as sending isEmpty() to them (which you also can't do with null references).

Put it this way. If after applying the NullObject pattern you still have some tests of the form p.isNull(), you can replace them with type-tests like (p instanceof NullObjectCLass). And then you can replace the type-test with a visitor. --


CategoryNull


EditText of this page (last edited December 11, 2009) or FindPage with title or text search