Оказывается, что instanceof работает быстрее, чем шаблон посетителя, представленный выше; Я думаю, что это должно вызвать у нас вопрос: действительно ли шаблон посетителя более элегантен, чем instanceof, когда он делает то же самое медленнее с большим количеством строк кода?
Вот мой тест. Я сравнил 3 метода: приведенный выше шаблон посетителя, instanceof и поле явного типа в Animal.
ОС: Windows 7 Enterprise SP1, 64-разрядная
Процессор: Intel (R) Core (TM) i7 CPU 860 @ 2,80 ГГц 2,93 ГГц
Оперативная память: 8,00 ГБ
JRE: 1.7.0_21-b11, 32-битный
import java.util.ArrayList;
import java.util.List;
public class AnimalTest1 {
public static void main(String[] args) {
Animal lion = new Lion("Geo");
Animal deer1 = new Deer("D1");
Animal deer2 = new Deer("D2");
List<Animal> li = new ArrayList<Animal>();
li.add(lion);
li.add(deer1);
li.add(deer2);
int reps = 10000000;
long start, elapsed;
start = System.nanoTime();
for (int i = 0; i < reps; i++) {
for (Animal a : li)
a.accept(new ActionVisitor()); // <-- Accept / visit.
}
elapsed = System.nanoTime() - start;
System.out.println("Visitor took " + elapsed + " ns");
start = System.nanoTime();
for (int i = 0; i < reps; i++) {
for (Animal a : li) {
if (a instanceof Lion) {
((Lion) a).roar();
} else if (a instanceof Deer) {
((Deer) a).runAway();
}
}
}
elapsed = System.nanoTime() - start;
System.out.println("instanceof took " + elapsed + " ns");
start = System.nanoTime();
for (int i = 0; i < reps; i++) {
for (Animal a : li) {
switch (a.type) {
case Animal.LION_TYPE:
((Lion) a).roar();
break;
case Animal.DEER_TYPE:
((Deer) a).runAway();
break;
}
}
}
elapsed = System.nanoTime() - start;
System.out.println("type constant took " + elapsed + " ns");
}
}
abstract class Animal {
public static final int LION_TYPE = 0;
public static final int DEER_TYPE = 1;
String name;
public final int type;
public Animal(String name, int type) {
this.name = name;
this.type = type;
}
public abstract void accept(AnimalVisitor av); // <-- Open up for visitors.
}
class Lion extends Animal {
public Lion(String name) {
super(name, LION_TYPE);
}
public void roar() {
// System.out.println("Roar");
}
public void accept(AnimalVisitor av) {
av.visit(this); // <-- Accept and call visit.
}
}
class Deer extends Animal {
public Deer(String name) {
super(name, DEER_TYPE);
}
public void runAway() {
// System.out.println("Running...");
}
public void accept(AnimalVisitor av) {
av.visit(this); // <-- Accept and call visit.
}
}
interface AnimalVisitor {
void visit(Lion l);
void visit(Deer d);
}
class ActionVisitor implements AnimalVisitor {
public void visit(Deer d) {
d.runAway();
}
public void visit(Lion l) {
l.roar();
}
}
Результаты испытаний:
Посетитель взял 920842192 нс
instanceof взял 511837398 нс
тип константы занял 535296640 нс
Этот шаблон посетителя вводит 2 дополнительных вызова метода, которые не нужны в instanceof. Вероятно, поэтому он медленнее.
Не то, чтобы производительность была единственным соображением, но обратите внимание на то, что 2 instanceofs быстрее, чем даже двухзначный оператор переключения. Многие люди беспокоятся о производительности instanceof, но это должно положить конец беспокойству.
Как Java-разработчик, я расстраиваюсь, когда люди догматично относятся к тому, чтобы избегать использования instanceof, потому что в моей работе несколько раз я хотел очистить или написать новый чистый код, используя instanceof, но коллеги / начальство не одобрило этот подход, потому что они более или менее слепо приняли идею, что instanceof никогда не должен использоваться. Я расстроен, потому что этот момент часто доводится до ума примерами игрушек, которые не отражают реальных деловых проблем.
Всякий раз, когда вы занимаетесь модульным проектированием программного обеспечения, всегда будут моменты, когда решения, зависящие от типа, должны быть изолированы от рассматриваемых типов, чтобы у типов было как можно меньше зависимостей.
Этот шаблон посетителей не нарушает модульность, но он не является превосходной альтернативой instanceof.