Реализация посетителя с использованием 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 ответа

Решение
  1. Если вы сделаете это, у вас больше не будет посетителя, у вас будет какой-то процессор. Ваш код будет просто итерацией списка, где каждый раз, когда проходит цикл, вы передаете то, что раньше использовали accept на процессор, который был формально посетителем. Вместо того, чтобы посетитель посетил посещенное, вы в некотором смысле перевернули эту парадигму; посетитель становится посетителем, так как он первоначально передается работнику. Вы могли бы сделать это; Вы бы не назвали это посетителем.

  2. Обычная мудрость обычно диктует, что использование 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!");
   }
 }

Вот источник.

Другие вопросы по тегам