Si scopre che instanceof è più veloce del modello di visitatore presentato sopra; Penso che questo dovrebbe farci dubitare, il modello di visitatore è davvero più elegante di instanceof quando sta facendo la stessa cosa più lentamente con più linee di codice?
Ecco il mio test. Ho confrontato 3 metodi: il modello di visitatore sopra, instanceof e un campo di tipo esplicito in Animal.
OS: Windows 7 Enterprise SP1 a 64 bit
Processore: Intel (R) core (TM) i7 CPU 860 @ 2.80 GHz 2.93 GHz
RAM: 8.00 GB
JRE: 1.7.0_21-B11, a 32 bit
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();
}
}
I risultati dei test:
Visitor preso 920842192 ns
instanceof preso 511.837.398 ns
tipo costante ha preso 535296640 ns
Questo modello visitatore introduce 2 chiamate di metodo in più che non sono necessari con instanceof. Questo è probabilmente il motivo per cui è più lento.
Non che le prestazioni siano l'unica considerazione, ma si noti come 2 instanceofs sono più veloci persino di un'istruzione switch di 2 casi. Un sacco di persone si sono preoccupate delle prestazioni di instanceof, ma questo dovrebbe far riposare la preoccupazione.
Come sviluppatore Java, mi sento frustrato quando le persone hanno un atteggiamento dogmatico sull'evitare l'uso di instanceof, perché ci sono state diverse volte nel mio lavoro che volevo pulire o scrivere un nuovo codice pulito usando instanceof, ma colleghi/i superiori non approvano questo approccio, perché hanno accettato più o meno ciecamente l'idea che l'instanceof non dovrebbe mai essere usata. Mi sento frustrato perché questo punto è spesso portato a casa con esempi di giocattoli che non riflettono le reali preoccupazioni di business.
Ogni volta che si persegue la progettazione di software modulare, ci saranno sempre momenti in cui le decisioni dipendenti dal tipo devono essere isolate dai tipi in questione, in modo che i tipi abbiano meno dipendenze possibili.
Questo modello di visitatore non interrompe la modularità, ma non è un'alternativa superiore a instanceof.
I metodi devono avere nomi diversi (ruggire, runAway)? L'idea del polimorfismo sta avendo gli stessi metodi nelle sottoclassi, con la JVM che chiama quella appropriata. – dariopy
Esiste un modo migliore per fare una cosa sbagliata? – InsertNickHere
Qual è l'* intento * del codice in 'TestAnimals'? È "fare la cosa specifica per ogni animale"? O ''ruggisci' tutto' Lion's,' runAway' all 'Deer'' e niente di più? – AakashM