È una cattiva pratica di programmazione verificare se una classe a cui fa riferimento la sua interfaccia è un'istanza di un'altra classe?

6

Ho una classe ( Timer ) con un elenco di matrici di oggetti Timable . Timeable è un'interfaccia. C'è una funzionalità specifica di cui ho bisogno per la classe Trigger (implementa Timable ), che ha un riferimento chiamato target . Molti metodi devono cercare nell'array Timer per Trigger oggetti con un determinato target .

Quello che ho fatto è stata una funzione come questa:

public Timable[] findObjectsWithTarget(Cue target) {
    ArrayList<Timable> result = new ArrayList<Timable>();
    for (Timable timed : fireable) { //fireable is the array (actually a HashSet) of Timed objects
        if (timed instanceof Trigger && ((Trigger) timed).getTarget() == target)
            result.add(timed);
    }
    return (Timable[]) result.toArray();
}

Sembra una cattiva pratica. La classe Timer ora dipende dalla classe Trigger (sorta di) e non è più generalizzata.

Quindi, le mie due domande:

1) In realtà è una cattiva pratica? Perché o perché no?

2) Qual è un modo migliore se si tratta di una cattiva pratica?

    
posta sinθ 05.06.2013 - 15:40
fonte

4 risposte

9

Sì, questa è davvero una cattiva pratica. Stai utilizzando un'interfaccia per astrarre il codice dall'implementazione. Quando lavori con l'interfaccia e accedi all'implementazione (tramite casting) stai violando la definizione dell'interfaccia.

Per risolvere questo tipo di problema, estendi la tua interfaccia con un altro metodo, al quale accederai dal tuo codice.

public Timable[] findObjectsWithTarget(Cue target) {
    ArrayList<Timable> result = new ArrayList<Timable>();
    for (Timable timed : fireable) { //fireable is the array (actually a HashSet) of Timed objects
        if (timed.hasTarget(target))
            result.add(timed);
    }
    return (Timable[]) result.toArray();
}

Devi implementare tale metodo anche in Timer class, ma puoi restituire false per impostazione predefinita.

    
risposta data 05.06.2013 - 15:48
fonte
8

Sì, è una cattiva pratica, perché stai aggiungendo funzionalità all'interfaccia Timeable che non stai dichiarando espressamente, vale a dire la possibilità di essere anche Trigger . Ci sono diversi modi per farlo meglio:

  • Aggiungi esplicitamente la funzionalità all'interfaccia Timeable , come in la risposta di woni . È meglio che usare instanceof , ma viola ancora il principio di separazione delle interfacce .
  • Mantieni elenchi separati di Timeable e Trigger oggetti nel codice chiamante, o almeno, mantieni un elenco combinato solo in contesti in cui la funzionalità Trigger non è necessaria.
  • Mantieni un riferimento arretrato dal tuo target in tutti gli oggetti Timeable che lo utilizzano. In altre parole, chiama target.getTriggers() anziché timeableList.findObjectsWithTarget(target) . Spesso quando un metodo si sente imbarazzante, è perché lo hai inserito nella classe sbagliata. Il fatto che "molti metodi" debbano fare questa ricerca significa che questo approccio avrebbe anche significativi vantaggi in termini di efficienza temporale.
risposta data 05.06.2013 - 17:20
fonte
1

1) Is it actually a bad practice?

Si

Why or why not?

A causa dell'accoppiamento a ritroso tra Timer e Trigger. In base alla progettazione, Timer non conosce nulla oltre a Timeable.

2) What's a better way if it is a bad practice?

Il tuo requisito non è esattamente chiaro. Il problema è trovare tutti i trigger per un dato target collegati a un timer specifico o trovare tutti i trigger per un target collegato a un timer? Ad ogni modo, ho un paio di idee:

  • Sposta "findObjectsWithTarget" fuori dal timer e in Trigger. Ciò interrompe l'accoppiamento tra Timer e Trigger, ma potrebbe richiedere l'aggiunta di un metodo a Timer che restituisce tutte le istanze "Timeable" associate.

  • Registra il target - > associazioni di trigger nella classe Target.

risposta data 05.06.2013 - 17:20
fonte
-1

Implementerei qualcosa come Target.searchTriggersForTarget(List<Timeable>, Cue) : List<Timeable> . Target dipende già da Timeable , quindi non ci sono nuovi accoppiamenti, e ora hai la tua logica specifica di Target nel posto giusto. Basta passare il tuo elenco di Timeable s da Timer e restituire il risultato. Ciò faciliterà anche l'uso della stessa logica in altre classi che contengono elenchi di Timeable s, il che risulta utile poiché si dice che sia usato in molti contesti.

    
risposta data 05.06.2013 - 17:57
fonte

Leggi altre domande sui tag