Java-Casting di classe dinamica dall'interfaccia all'implementazione


Ho letto altri post correlati, ma non sono ancora del tutto sicuro di come, o se è possibile lanciare dinamicamente (interfaccia all'implementazione) in Java. Ho l'impressione di dover ricorrere alla riflessione per farlo.

Il particolare progetto su cui sto lavorando richiede un utilizzo di molti instanceof controlli, ed è - a mio parere - un po ' fuori mano, quindi apprezzerei qualsiasi idea/soluzione.

Di seguito è riportato un mini esempio che ho scritto solo per chiarire esattamente cosa voglio fare. Fatemi sapere se avete bisogno di ulteriori informazioni:

Interfaccia:

public interface IRobot {
    String getName();
}

Implementazioni:

public class RoboCop implements IRobot {
    String name = this.getClass()+this.getClass().getName();
    public RoboCop() {}
    public String getName() { return name; }
}

public class T1000 implements IRobot {
    String name = this.getClass()+this.getClass().getName();
    public T1000() {}
    public String getName() { return name; }
}

La classe che gestisce le implementazioni:

import java.util.LinkedList;
import java.util.List;
public class RobotFactory {

    public static void main(String[] args) {
        new RobotFactory();
    }

    public RobotFactory() {
        List<IRobot> robots = new LinkedList<IRobot>();
        robots.add( new RoboCop() );
        robots.add( new T1000() );
        System.out.println("Test 1 - Do not cast, and call deploy(robot)");
        for(IRobot robot : robots) {
            deploy(robot);  // deploy(Object robot) will be called for each..
        }
        System.out.println("Test 2 - use instanceof");
        for(IRobot robot : robots) { // use instanceof, works but can get messy
            if(robot instanceof RoboCop) {
                deploy((RoboCop)robot);
            }
            if(robot instanceof T1000) {
                deploy((T1000)robot);
            }
        }
        System.out.println("Test 3 - dynamically cast using reflection?");
        for(IRobot robot : robots) {
            //deploy((<Dynamic cast based on robot's type>)robot);  // <-- How to do this?
        }
    }

    public void deploy(RoboCop robot) {
        System.out.println("A RoboCop has been received... preparing for deployment.");
        // preparing for deployment
    }

    public void deploy(T1000 robot) {
        System.out.println("A T1000 has been received... preparing for deployment.");
        // preparing for deployment
    }

    public void deploy(Object robot) {
        System.out.println("An unknown robot has been received... Deactivating Robot");
        // deactivate
    }
}

Uscita:

[RoboCop@42e816, T1000@9304b1]
Test 1 - Do not cast, and call deploy(robot)
An unknown robot has been received... Deactivating Robot
An unknown robot has been received... Deactivating Robot
Test 2 - use instanceof
A RoboCop has been received... preparing for deployment.
A T1000 has been received... preparing for deployment.
Test 3 - dynamically cast using reflection?

Quindi, per riassumere la mia domanda, come posso evitare completamente di dover usare instanceof in questo caso. Grazie.

Author: jpaugh, 2010-10-07

5 answers

È possibile distribuire un metodo di IRoboto utilizzare il modello di visitatore .

E no, la riflessione non renderà le cose più facili qui.

 7
Author: meriton, 2010-10-06 20:52:23

Kent Beck dice nel suo libro Test Driven Development : Ogni volta che si utilizza il controllo del tipo di runtime, il polimorfismo dovrebbe aiutare. Metti il metodo deploy () nella tua interfaccia e chiamalo. Sarete in grado di trattare tutti i vostri robot in modo trasparente.

Dimentica la riflessione, stai solo pensando. Ricorda i tuoi principi di base orientati agli oggetti.

 3
Author: Mike, 2010-10-06 20:54:42

L'invio di metodi sovraccaricati viene eseguito staticamente in compiletime, quindi il tuo approccio non può essere fatto funzionare. È anche un design molto cattivo. Non ti sembra strano che il metodo getName(), la cosa soloche differisce tra le classi robot, non sia mai in realtà chiamata?

Devi abbandonare i metodi sovraccaricati e utilizzare invece l'override dei metodi nelle classi robot, che chiami direttamente. cioè

public void deploy(IRobot robot) {
    System.out.println("A "+robot.getName()+" has been received..."
                        +" preparing for deployment.");
    // preparing for deployment
}
 3
Author: Michael Borgwardt, 2010-10-06 20:58:00

È possibile evitare instanceof spostando il metodo di distribuzione nell'interfaccia e nelle implementazioni iRobot.

La spiegazione del comportamento è che i tuoi tre metodi di distribuzione sono tre metodi diversi; metodi sovraccaricati con firme diverse. Al momento della compilazione, viene determinato quale viene scelto, non in fase di runtime in base alla classe reale...

 2
Author: ivy, 2010-10-06 20:53:09

Invece di usare instanceof è possibile utilizzare Modello di metodo di fabbrica

Definizione del metodo di fabbrica...

Come altri modelli creativi, esso affronta il problema della creazione oggetti (prodotti) senza specificare la classe esatta di oggetto che sarà creare.

Avrai bisogno di un RobotCreatorFactory che avrà un metodo chiamato IRobot createRobot(String robotName) {...} (visto che il tuo robot restituisce un nome. I miei suggerimenti sono che ogni robot avrà un public static String name NAME = Robocop.class.getName();. Dentro il metodo avrai un controllo come

if (Robocop.NAME.equals(robotName) { return new RoboCop(); }

In questo modo, allevii instanceof. E inoltre, puoi usare il consiglio di @ Meriton su un DeploymentVisitor (usando un modello di visitatore)....

PS Il mio esempio è una spiegazione approssimativa del modello del metodo di fabbrica. Un esempio esiste in GOF book e Wikipedia.

 0
Author: Buhake Sindi, 2010-10-06 21:24:46