Реализация посетителя с использованием InstanceOf
Я хорошо осваиваю шаблон посетителей. Однако мне интересно кое-что.
Наиболее важной мотивацией для использования шаблона посетителя является добавление логики, включающей определенные модели данных на стороне клиента, без необходимости проверки реального типа объекта данных. Техника, используемая для решения, называется Double-Dispatching.
Итак, здесь фрагмент кода модели данных, реализующей accept()
метод:
public class Ferrari extends Car {
//......
@Override
public void accept(Visitor v){
v.visit(this);
}
}
И здесь PrintCarVisitor
реализации Visitor
интерфейс:
public class PrintCarVisitor implements Visitor {
//...
@Override
public void visit(Ferrari c){
System.out.println("Ferrari");
}
}
Следовательно, нет if/else
серия и instanceof
серия нужна.
Любой клиент будет:
Visitor visitor = new PrintCarVisitor();
car.accept(visitor); //no need to know the exact Car's type
Тем не менее, поскольку Visitor не поддерживает принцип Open/Closed (поскольку новая модель данных приводит к разрыву класса путем добавления его собственного visit
метод), зачем нам двойная диспетчеризация?
Разве мы не можем просто изолировать if/else
серия в рамках реализации посетителя.
С этой гипотетической альтернативой эта часть кода исчезнет:
public class Ferrari extends Car {
//This method is not needed anymore with this alternative
@Override
public void accept(Visitor v){
v.visit(this);
}
}
PrintCarVisitor
было бы:
public class PrintCarVisitor {
public void visit(Car c){
if(c instanceof Ferrari){
System.out.println("Ferrari");
}
}
}
С этой альтернативой каждый вызывающий оператор все равно будет иметь дело с абстракцией модели данных, например:
new PrintCarVisitor().visit(car); //no need to know the exact data type in client side
Априори, этот второй подход хорош, так как он не включает в себя весь шаблон, который генерируется во время реализации чистого шаблона посетителя.
Я полагаю, что у этого подхода есть два недостатка:
1) Там нет гарантии (как Visitor
интерфейс навязывает), что любой используемый посетитель располагает методом, соответствующим Car
в настоящее время лечится.
2) код BoilerPlate остается более тяжелым в классе реализации Visitor с серией instanceof
а также casting
,
Являются ли они какими-либо другими недостатками, объясняющими, почему шаблон посетителя ДОЛЖЕН использовать двойную диспетчеризацию и поэтому не может просто изолировать instanceof
ряд внутри класса (как статический Factory
делает например)?
2 ответа
Если вы сделаете это, у вас больше не будет посетителя, у вас будет какой-то процессор. Ваш код будет просто итерацией списка, где каждый раз, когда проходит цикл, вы передаете то, что раньше использовали
accept
на процессор, который был формально посетителем. Вместо того, чтобы посетитель посетил посещенное, вы в некотором смысле перевернули эту парадигму; посетитель становится посетителем, так как он первоначально передается работнику. Вы могли бы сделать это; Вы бы не назвали это посетителем.Обычная мудрость обычно диктует, что использование
instanceof
должны быть зарезервированы для последнего средства. Почему вы используетеinstanceof
, когда вы можете позволить полиморфизму Java сделать это для вас? Одним из пунктов наличия объектов является это преимущество. Если вы делаете это, почему бы не отказаться от переопределения методов и просто использоватьinstanceof
определить, что делать в методах класса, вместо того, чтобы полагаться на динамическую диспетчеризацию, в случае переопределения методов?
Проект Xtext имел ту же проблему, и они создали вспомогательный класс PolymorphicDispatcher
, В двух словах, PolymorphicDispatcher
выполняет во время выполнения то, что делает компилятор во время компиляции: найти метод, который наилучшим образом соответствует набору параметров, и вызвать его.
Таким образом, ваш посетитель будет выглядеть так:
public class PrintCarVisitor {
public void visit(Car c){
System.out.println("Just a car");
}
public void visit(Ferrari c){
System.out.println("A Ferrari!");
}
}