Would you say that Objective-C categories are an implementation of the visitor design pattern?
No, Objective-C categories are not an implementation of the visitor pattern. Categories don't really have an exact match in the design pattern world, since the technique of injecting methods into existing classes without subclasses isn't possible in most languages. I'd say it's closer to the decorator pattern, but that pattern is usually implemented with composition, i.e. by wrapping an instance of the object you want to "enhance".
The visitor pattern is useful for encapsulating algorithm logic that may be applied to a variety of objects, structs, etc. For example, if you want to create HTML output for a graph of objects, you could (A) write a htmlString
method on each object and call it for each object, or (B) use the visitor pattern and create a concrete visitor that knows how to produce HTML output for each node it visits.
The former approach is more generic, and the logic for task T is scattered in small chunks across classes X, Y and Z. The latter approach places all the related code in a single visitor object, which tends to simplify maintenance and prevent the "I forgot that one class..." problem. However, the visitor pattern is arguably somewhat heavy-handed for simple situations — where it really pays off is when you have several different parallel functionalities and wish to abstract the logic from the classes on which the functionality is being performed. For example, you might implement other visitors which produce PDF or RTF output, etc. Each visitor can take care of recursion and calling its own visit methods in the order necessary, and separate visitors can use a completely distinct order.
It should be noted that in many languages, the visitor pattern uses method overloading (same name, different signature/arguments). Since Objective-C doesn't allow method overloading, you must use distinct method names, but this can actually help avoid bugs caused by not knowing which overloading is being called.
Categories can be used to implement the visitor pattern.
@protocol Visit
- (void)acceptVisitor:(MyVisitor *)visitor;
@end
@interface Foo (Visit) <Visit>
@end
@interface Bar (Visit) <Visit>
@end
@implementation MyVisitor
- (void)visit:(id)someObject {
if ([someObject conformsToProtocol:@protocol(Visit)]) {
[(id<Visit>)someObject acceptVisitor:self];
}
}
- (void)visitFoo:(Foo *)foo { ... }
- (void)visitBar:(Bar *)bar { ... }
@end
@implementation Foo (Visit)
- (void)acceptVisitor:(MyVisitor *)visitor {
[visitor visitFoo:self];
}
@end
@implementation Bar (Visit)
- (void)acceptVisitor:(MyVisitor *)visitor {
[visitor visitBar:Self];
}
@end
This is IMO neater than the classic GoF Visitor design, as there's no pollution of the visited classes' public interfaces and the whole thing can be encapsulated in the visitor class's compilation unit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With