Strategie di ordinamento configurabili

2

Devo essere in grado di ordinare un oggetto in base a più condizioni e questi tipi devono essere configurabili dal file application.properties (come in esso dovrebbe essere possibile specificare nel file gli ordinamenti da applicare e l'ordine).

Quindi nel mio design ho creato un comparatore per ciascuno di questi tipi, quindi un enumerato con un fornitore come:

public enum CarSort {
    ENGINE(CarEngineComparator::new), BRAND(CarBrandComparator::new);

    private final Supplier<Comparator<Car>> constructor;

    CarSort(Supplier<Comparator<Car>> constructor){
        this.constructor = constructor;
    }

    Comparator<Car> newComparator() {
        return constructor.get();
    }
}

Con questo design, sarei quindi in grado di caricare i tipi da un file delle proprietà:

myapp.cars.sorts=BRAND,ENGINE

E concatenati con qualcosa del tipo:

    Stream<Comparator<Car>> comparators = sorts.stream().map(CarSort::newComparator);
    return comparators
            .reduce(Comparator::thenComparing)
            .map(comparator -> filteredCars.sorted((comparator.reversed())))
            .orElse(filteredCars)
            .collect(Collectors.toList());

Il problema che ho al momento è che uno dei comparatori richiede due parametri, quindi non credo che questa soluzione regga più, ma al momento non riesco a pensare ad alternative pulite.

Sai se sarebbe possibile adattare il mio attuale design per soddisfare questo requisito o disporre di una soluzione alternativa valida?

EDIT: ho intenzione di aggiungere ulteriori dettagli. Diciamo che il comparatore specifico che ho bisogno di aggiungere tipi di auto in base ad esempio su quanto sono lontani dal client. Quindi potrebbe essere qualcosa del genere (i dettagli non sono importanti in realtà, solo il fatto che ha bisogno di un parametro aggiuntivo):

public class CarDistanceComparator implements Comparator<Car> {

    private Location origin;

    CarDistanceComparator(Location origin) {
        this.origin = origin;
    }

    @Override
    public int compare(Car o1, Car o2) {
        double distanceToFirstCar = DistanceService.calculateDistance(origin, o1.getLocation());
        double distanceToSecondCar = DistanceService.calculateDistance(origin, o2.getLocation());
        return Double.compare(distanceToFirstCar, distanceToSecondCar);
    }
}

Quindi quello che vorrei è essere in grado di ordinare per BRAND, ENGINE, DISTANCE (come specificato nel file di configurazione) per più clienti. Significa che dovrei passare ogni volta un argomento diverso a DistanceComparator.

    
posta aynd 04.09.2018 - 06:22
fonte

1 risposta

1

Il tuo approccio funzionerebbe correttamente se la creazione dei tuoi comparatori fosse semplicemente una questione di creazione di una nuova istanza di esso. Ora che hai bisogno di più parametri, questo complica il problema. Ciò che rende questo complesso è che enum si occupa di un certo numero di possibilità. Aggiungendo un parametro, questo non è più il caso, o se insisti a usare le enumerazioni, dovresti complicare la configurazione senza necessità per farlo funzionare comunque.

Considera l'alternativa possibile:

Diciamo che abbiamo una fabbrica ComparatorFactory. Quando hai bisogno di un nuovo comparatore, chiami semplicemente il suo metodo getComparator che passa il nome. ComparatorFactory cercherà diverse proprietà basate su quel nome:

comparator.<name>=<full comparator class name with parameterless constructor>
comparator.<name>.params=param1,param2,param3
comparator.<name>.params.param1=<param1value>
comparator.<name>.params.param2=<param2value>
comparator.<name>.params.param3=<param3value>

Quindi qui sostituisci <name> con il nome del comparatore in questione. Tutti i parametri saranno elencati in comparator.<name>.params (o assenti nel qual caso non ce ne sono).

Per ogni parametro trovato, dopo la creazione iniziale dell'istanza, il tuo ComparatorFactory cercherebbe di assegnargli il valore usando un setter con lo stesso nome (c'è un sacco di supporto per i bean nativi per tali cose senza troppe difficoltà).

In questo modo è completamente configurabile e trasparente nel tuo programma, che è ciò che desideri. E, se preferisci, puoi associare un enum a ciascun nome del comparatore per rendere più facile la chiamata al tuo programma.

Questo approccio ha anche il vantaggio che i tuoi Comparatori possono essere messi nella propria libreria e non devono essere referenziati direttamente dal tuo progetto. Deve essere presente nel classpath solo all'avvio del programma.

    
risposta data 04.09.2018 - 08:35
fonte

Leggi altre domande sui tag