Поскольку Visitor
s не знает, как перемещаться по закрытым внутренним полям составного Object
.
Если бы вы позвонили Visitor.visit(something)
, то пришлось бы выяснить, было ли это что-то частнымполя, которые нуждались в поперечном.Для этого вам нужно, чтобы something
принял ваш Visitor
.Как только вы решите, что навигация должна быть в посещенных объектах (а не в Visitor
), вы поймете, что вам нужен обратный вызов к Visitor
, чтобы сообщить ему, каков следующий элемент в пути навигации.Обычно это метод accept(...)
;однако, если вы попытались сделать accept(...)
просто оболочкой для инициирования навигации (путем делегирования к параметру), тогда вам понадобится второй набор методов, чтобы сообщить Visitor
, что вы вводите X сейчас, а вводу Y сейчас.
Используя подход GOF, можно безопасно разделить посещаемый элемент на подклассы и изменить путь посещения, чтобы включить или пропустить дополнительные поля.Это не повлияет на существующие Visitor
s, потому что их интерфейс не изменится.Нет необходимости перекомпилировать подклассы Visitor
.
Используя предложенный вами подход, когда вы добавляете новый тип в иерархию посещаемых элементов, нужно будет перекомпилироватьвсе посетители, даже посетители не интересовались новым типом.
Хороший компромисс был бы:
public interface Visitable {
public void accept(Visitor v);
}
, если бы все ваши "иерархия данных" реализовали Visitable, а ваш посетитель«удобный метод», подобный этому
public abstract class Visitor {
public void initiate(Visitable v) {
v.accept(this);
}
public abstract void accept(...);
public abstract void accept(...);
public abstract void accept(...);
}
Но вам решать, предпочтительнее ли иметь интерфейс для такого базового класса.Для меня я бы предпочел более слабосвязанный интерфейс, но мнения расходятся.