Reputation: 1006
I had a problem to extend Visitor pattern accross multiple modules, you can read explanation of the issue in the comments.
interface Example {
interface ISource {
Object accept(ISourceVisitor visitor);
}
class Module1Source1 implements ISource {
@Override
public Object accept(ISourceVisitor visitor) {
return visitor.visit(this);
}
}
class Module1Source2 implements ISource {
@Override
public Object accept(ISourceVisitor visitor) {
return visitor.visit(this);
}
}
interface ISourceVisitor {
Object visit(Module1Source1 wheel);
Object visit(Module1Source2 engine);
}
class SupportedCurrenciesVisitor implements ISourceVisitor {
@Override
public Object visit(Module1Source1 wheel) {
return ImmutableList.of("USD");
}
@Override
public Object visit(Module1Source2 engine) {
return ImmutableList.of("EUR");
}
}
//suppose we don't want to change the code above because it's in another library
//I want to add one more source
class Module2Source1 implements ISource {
@Override
public Object accept(ISourceVisitor visitor) {
return null;
}
}
// I cannot change ISourceVisitor, so what do I need to do?
// one way is to create another interface
interface IAnotherModuleSource extends ISource {
Object accept(IThisModuleSourceVisitor visitor);
}
interface IThisModuleSourceVisitor extends ISourceVisitor {
Object visit(Module2Source2 module2Source2);
}
class Module2Source2 implements IAnotherModuleSource {
//it's ok
@Override
public Object accept(IThisModuleSourceVisitor visitor) {
return visitor.visit(this);
}
//but what should we do with this:??
@Override
public Object accept(ISourceVisitor visitor) {
return accept((IThisModuleSourceVisitor) visitor);
}
//this way if SupportedCurrenciesVisitor will be passed to the Module2Source2 we
//will have CCE
//but it's ok if we pass here specific visitor for this module
}
}
Apparently if we would place the method getSupportedCurrencies() in ISource there would be no such problem, but it's also not perfect way I suppose.
The question is can we do better with Visitor?
Or what would be your suggested approach in this situation?
Upvotes: 1
Views: 1151
Reputation: 3273
Tl;dr: The problem that you're describing is a result of trying to apply the pattern to a problem that it is not suited for.
"Reasons to change" all fall into 2 basic categories: data and functionality. The visitor pattern decouples the normal OO direct association between the data and functionality in a way that lets you sacrifice the ability to change the data structure in exchange for being able to change functionality very easily. It would be trivial to add another ISourceVisitor
because that's what the Visitor pattern is designed to let you do. Adding another ISource
changes your data structure and forces you to change all of your functionality to accommodate it, which is a sure sign that you picked the wrong pattern.
Upvotes: 1