Passare un elenco di oggetti della classe base a un metodo e utilizzare instanceof per filtrarli

2

Sto lavorando a un progetto di reverse engineering Java, in cui sto analizzando bytecode e sto cercando di identificare classi, metodi e campi utilizzando il ASM quadro. Dopo aver identificato questi, trasformo alcune classi per implementare le mie interfacce personalizzate con, ad esempio, i metodi getter.

Per identificare le classi, ho creato una classe astratta Analyzer con un metodo astratto per eseguire l'analisi su una raccolta di classi. Le classi secondarie di Analyzer cercano di identificare una determinata classe / campo / metodo di interesse. Ad esempio, AnimalAnalyzer tenta di identificare la classe che rappresenta un animale.

Dopo aver eseguito gli analizzatori, inserisco le mie interfacce e le loro implementazioni nelle classi. Analogamente a Analyzer , ho creato un'interfaccia Injector con un metodo inject(List<Analyzer> analyzers) . Per ogni classe che voglio iniettare c'è una classe separata che implementa l'interfaccia, ad esempio AnimalInjector . L'iniettore itera l'elenco degli analizzatori, che contengono i risultati della loro analisi e utilizza quelli rilevanti controllandone il tipo con l'operatore instanceof . Per "quelli rilevanti" intendo gli analizzatori che contengono le informazioni necessarie per generare il bytecode con.

public abstract class Analyzer {    
    public abstract void run(ClassCollection classCollection);
}

public class AnimalAnalyzer extends Analyzer {
    private ClassNode identifiedClass = null;
    @Override
    public void run(ClassCollection classCollection) {
        for (ClassNode classNode : classCollection.getAllClassNodes()) {
             boolean isMatch = doesClassMatch(classNode); // method omitted from example
             if (isMatch) {
                 identifiedClass = classNode;
                 break;
             }
        }
    }
}
   
public interface Injector {
    void inject(List<Analyzer> analyzers);
}

public class AnimalInjector implements Injector {
    @Override
    public void inject(List<Analyzer> analyzers) {
        AnimalAnalyzer animalAnalyzer = null;
        for (Analyzer analyzer : analyzers) {
            if (analyzer instanceof AnimalAnalyzer) {
                animalAnalyzer = (AnimalAnalyzer) analyzer;
            }
        }
        if (animalAnalyzer == null) {
            throw new IllegalStateException("AnimalAnalyzer not found in list");
        }
        ... (bytecode generation and injection)
    }
}

Uno dei motivi per cui ho scelto di farlo in questo modo è che gli analizzatori devono essere eseguiti in un ordine specifico. Ad esempio, per identificare un Dog che estende Animal , quest'ultimo deve essere trovato per primo. A tale scopo ho creato MasterAnalyzer , che memorizza un elenco di analizzatori in un ordine specifico. Quando run viene richiamato su questo analizzatore, richiama l'esecuzione sugli analizzatori nell'elenco nel giusto ordine.

public class MasterAnalyzer extends Analyzer {
    private List<Analyzer> analyzers = new ArrayList<>();

    public MasterAnalyzer() {
        analyzers.add(new AnimalAnalyzer());
        analyzers.add(new DogAnalyzer());
        ... (more analyzers)
    }

    public List<Analyzer> getAnalyzers() {
        return analyzers;
    }

    @Override
    public void run(ClassCollection classCollection) {
        for (Analyzer analyzer : analyzers) {
            analyzer.run(classCollection);
        }
    }
}

Gli iniettori, a loro volta, possono essere tutti eseguiti con lo stesso comando: inject(masterAnalyzers.getAnalyzers()) . Mentre ciò rende più semplice dal punto di vista del cliente, fa sì che gli iniettori sappiano più del necessario. Ad esempio, supponiamo di avere anche CatAnalyzer . Ora, quando eseguiamo DogInjector , l'elenco degli analizzatori includerà CatAnalyzer , anche se DogInjector non gli interessa. Questo, insieme ai cattivi usi di instanceof , mi provoca segnali rossi. Ora sto contattando sviluppatori più esperti per alcuni suggerimenti su come migliorare il mio design. Durante la ricerca di modi alternativi, ho trovato il pattern Visitatori , che potrebbe essere applicabile qui. Tuttavia, non sono ancora sicuro di come applicarlo e se possa migliorare il mio design o meno.

    
posta Markus K. 18.12.2018 - 14:18
fonte

1 risposta

0

Se ho capito bene, la classe MasterAnalyzer contiene solo un elenco statico di tutti gli analizzatori disponibili e l'ordine di questa lista è relativo all'ordine di invocazione.

Inoltre, vuoi che gli Iniettori specifici ignorino determinati Analizzatori quando lo chiedono da MasterAnalyzer.

Ti consiglierò di rendere la classe MasterAnalyzer più intelligente, esponendo un'API per richiedere agli Analizzatori richieste specifiche (dati come parametro per esempio).

Ad esempio, se DogInjector vuole registrarsi solo ad analizzatori specifici dovrebbe ricevere un modo per ottenerli dall'analizzatore e invocarli solo; O da una regola / tabella-mappa / bitset ecc. Che segnala quale tipo di analizzatore vuole ricevere.

Esempi:

  • La regola può essere "analizzatori" con il nome della classe che inizia con "Cane".
  • Table-Map e Bitset per segnalare analizzatori specifici (ma rende il tuo iniettore dipendente dalla rappresentazione della lista, quindi potrebbe essere inappropriato).
  • Gli analizzatori themeseleves possono contenere informazioni / parametri registrati in base ad esso e puoi fornire una funzione lambda al MasterAnalyzer che invocherà su ogni analizzatore in elenco e aggiungere di conseguenza.

La soluzione più semplice è chiedere gli analizzatori specifici e ottenere una lista copiata con riferimenti per gli analizzatori che MasterAnalyzer li aggiunge nell'ordine appropriato.

* A seconda delle tue esigenze e delle dinamiche del tuo sistema puoi restituire un oggetto proxy alla lista degli analizzatori, che passerà 'gli analizzatori a cui non sei registrato e non avrai bisogno di copiare la lista.

    
risposta data 02.01.2019 - 16:03
fonte

Leggi altre domande sui tag