2015-09-17 24 views
7

Per esempio:Qual è il modo migliore per navigare in un albero complesso di oggetti dissimili?

class Vehicle { 
    Collection<Axle> axles; 
} 

class Axle { 
    Collection<Wheel> wheels; 
} 

class Wheel { 
    // I think there are dually rims that take two tires -- just go with it 
    Collection<Tire> tires; 
} 

class Tire { 
    int width; 
    int diameter; 
} 

Ho un servizio attraverso il quale posso ottenere una raccolta di tutti gli oggetti del veicolo che conosco circa. Ora dì che ho una gomma di una larghezza e di un diametro specifici, e voglio trovare un veicolo che possa prenderlo. Il modo più semplice è quello di avere una serie di quattro cicli annidati, in questo modo:

for (Vehicle vehicle : vehicles) { 
    for (Axle axle : vehicle.getAxles()) { 
     for (Wheel wheel : axle.getWheels()) { 
      for (Tire tire : wheel.getTires()) { 
       if (tire.width == targetWidth 
       && tire.diameter == targetDiameter) { 
        // do something 
        break; 
       } 
      } 
     } 
    } 
} 

C'è un modello buon design per questo? O una migliore struttura dati da usare? Sarebbe meglio tenere semplicemente un indice da qualche parte delle informazioni sui pneumatici mappate ai veicoli?

modificare: rispondendo alle domande dei commenti

Avete controllo sulla struttura dei dati si ricevono dal servizio?

Avete bisogno di cercare pneumatici diversi più volte nello stesso dei dati?

è la prestazione un problema?

Non particolarmente

Quando si trova il pneumatico, ti basta sapere quale veicolo lo contiene o hai bisogno anche l'asse e la ruota?

A volte solo il veicolo, a volte solo l'asse - due contesti diversi

Avete bisogno il riferimento al pneumatico che è stato trovato?

Sì, nei casi in cui ho bisogno l'asse

EDIT2: Estendendo ulteriormente la metafora, per spiegare i due contesti sopra:

Contesto 1 - Voglio sapere il veicolo, quindi posso inviare un lavoratore per ritirare il veicolo e riportarlo indietro

Contesto 2 - Voglio conoscere l'asse e la gomma, perché sono al veicolo che prova a fare il lavoro

+0

Dipende ... Hai controllare la struttura dei dati che ricevi dal servizio? Hai bisogno di cercare pneumatici diversi più volte con gli stessi dati? Le prestazioni sono un problema? Quando trovi il pneumatico, hai solo bisogno di sapere quale veicolo lo contiene o hai bisogno anche dell'asse e della ruota? Hai bisogno del riferimento alla gomma che è stata trovata? – Cinnam

+0

Ho risposto al post. Grazie per aver guardato! –

+0

Questo problema mi sembra correlato alla Legge di Demetra (che è più un principio che una "legge"). Vedi http://stackoverflow.com/q/12284057/1168342 – Fuhrmanator

risposta

2

È possibile appiattire i loop utilizzando Java 8 streams.

vehicles.stream() 
    .flatMap(vehicle -> vehicle.getAxles().stream()) 
    .flatMap(axle -> axle.getWheels().stream()) 
    .flatMap(wheel -> wheel.getTires().stream()) 
    .filter(tire -> tire.width == targetWidth 
      && tire.diameter == targetDiameter) 
    .forEach(tire -> { 
     // do something 
    }); 

La cosa bella è che i flussi si potrebbe inserire ulteriore filter, filter, findAny, ecc, invita abbastanza facilmente in qualsiasi punto della sequenza.

+0

È possibile accedere al veicolo e all'asse contenenti quando si trova lo pneumatico o le classi devono avere alcuni riferimenti genitoriali per quello? – Cinnam

+0

@Cinnam - Non sembra - Ho appena creato un'app di test per l'esempio del veicolo, e il codice nel blocco forEach non ha potuto fare riferimento a nulla sopra la gomma. –

0

Fintanto che non ci sono troppi elementi e/o prestazioni non è un grosso problema, probabilmente andrei semplicemente con i cicli nidificati (o flussi dalla risposta di John).

Dal momento che si dispone di due contesti per la ricerca, si potrebbe passare l'azione appropriata per il metodo di ricerca - una cosa del genere (usando loop in questo caso):

interface TireAction { 
    void doSomething(Vehicle v, Axle a, Tire t); 
} 

void findTireAndPerform(int targetWidth, int targetDiameter, TireAction action) { 
    for (Vehicle vehicle : vehicles) { 
     for (Axle axle : vehicle.getAxles()) { 
      for (Wheel wheel : axle.getWheels()) { 
       for (Tire tire : wheel.getTires()) { 
        if (tire.width == targetWidth && tire.diameter == targetDiameter) { 
         action.doSomething(vehicle, axle, tire); 
         break; 
        } 
       } 
      } 
     } 
    } 
} 

void someMethod() { 
    ... 

    findTireAndPerform(width, diameter, (v, a, t) -> { 
     // send worker to 'v' 
    }); 

    ... 

    findTireAndPerform(width, diameter, (v, a, t) -> { 
     // work on 'a' and 't' 
    }); 
} 
+0

Sì, più ci penso e più mi occupo di ricerca, più sto pensando di mantenere il ciclo. Si arrende, però. –

+0

@SamJones Beh, potrebbe non essere la cosa più bella :) Ma in questo caso non penso che sia inappropriato. – Cinnam

1

Senza cambiare la struttura di dati hai vinto' essere in grado di fare la differenza significativa È possibile aggiungere dello zucchero sintattico con lambda, ma è essenzialmente la stessa soluzione.

Le cose si poteva guardare:

  • Il modello permette di Vehicles con zero assi o cento. Sebbene dipenda dal tuo modello di business, sembra strano.
  • Il tuo modello consente di avere diversi assali nel tuo veicolo, diverse ruote. È davvero necessario? Assicurati che gli elementi del tuo modello debbano avere la loro identità separata (attualmente ogni oggetto ce l'ha) e che è solo un oggetto valore.
  • Assicurati di aver davvero bisogno di un modello così dettagliato. Attualmente hai due classi (Axle, Wheel), che contengono solo raccolte di oggetti interni. Se saranno solo semplici oggetti JavaBean con getAllInnerTypes(), dovresti prendere in considerazione la rimozione di questa classe. Potrebbe anche accadere che le informazioni sui pneumatici vengano archiviate quasi direttamente nella classe Vehicle.
2

vorrei invertire la logica e spostare la questione nella Vehicle, a meno che naturalmente si desidera mantenere i vostri oggetti sottili per qualsiasi altro motivo (nel qual caso io personalmente li avvolgono con un oggetto più spesso per aggiungere qualsiasi comportamento necessario)

class Vehicle { 
    ... 

    public Tire acceptsTire(Tire tire) { 

    } 
} 

da qui in avanti ci sono diverse possibilità, a seconda di quanto sia importante questo pezzo di logica di business è nel dominio in generale.

  1. Se avrete diverse azioni si potrebbe probabilmente solo iterare come aveva fatto nel campione. O forse allo stesso modo suggerito, mantenere la domanda in cascata sul componente corretto. Finché si può vivere con la complessità del tempo di fare questo dovrebbe andar bene.
  2. Se questo controllo è qualcosa che dovresti fare, potresti avere un riferimento al tipo di pneumatici che tieni direttamente nel veicolo, potrebbe essere la tua collezione Tire oppure potresti passare un'istanza TireSpecification quando costruisci lo Vehicle se per qualsiasi motivo hai bisogno di tenerle separate (la tua intenzione non è molto chiara nella domanda, è il pneumatico sulla macchina o solo una specifica di cosa potrebbe adattarsi?)
Problemi correlati