Quale modello di progettazione per una sequenza di processori sulla stessa lista?

2

Immagina di avere una lista di oggetti - come un elenco o una matrice di oggetti che rappresentano gli utenti - e due processori:

  • si aggiunge / elimina / modifica alcuni utenti in determinate circostanze
  • l'altro fa la stessa cosa controllando le altre condizioni

circostanze / condizioni sono verificate sfruttando altre classi / servizi ecc., che vengono iniettati quando vengono istanziati.

Voglio un'architettura che mi permetta di aggiungere / rimuovere processori con facilità. Immagino che un decoratore sia abbastanza buono per questo tipo di lavoro, ma forse un po 'rigido quando si tratta di aggiungere / rimuovere dinamicamente processori in fase di runtime.

Un'altra idea sta usando qualcosa come una catena di responsabilità, ma lasciando che ogni processore modifichi la lista. Non so se esiste una specie di schema come questo o se per qualche motivo è una cattiva pratica.

Fammi sapere cosa ne pensi: -)

    
posta Bertuz 25.03.2018 - 18:27
fonte

3 risposte

1

Ecco cosa farei:

Avere un'interfaccia chiamata Processor e consentire alle tue due classi concrete di implementarlo in base ai tuoi due punti elenco. Ogni classe dovrebbe gestire qualsiasi logica condizionale stessa e, di conseguenza, tutte le classi helper necessarie devono essere inserite nella classe che ne ha bisogno.

Crea una classe chiamata Processors (nota il finale s ) e inietta tutti gli oggetti che implementano l'interfaccia Processor . In primavera, questo può essere fatto facilmente iniettando List<Processor> . In questo corso avrai solo un metodo il cui unico contenuto sarà quello di scorrere l'elenco iniettato e applicare i processori. Se l'ordine è importante, invece di usare l'iniezione automatica, crea il bean esplicitamente in una classe di configurazione Spring, passandogli un elenco di processori nell'ordine corretto. In alternativa, potresti utilizzare l'annotazione @Order dei tuoi Processor di fagioli e l'iniezione automatica, ma questo è un po 'meno chiaro per qualcuno che legge il codice, a mio parere. Se l'ordine non è importante, lascia che l'iniezione automatica faccia tutto il lavoro.

Questa soluzione presenta i seguenti vantaggi: * Ogni processore è autonomo: contiene tutta la logica necessaria per applicare un tipo di trasformazione, inclusa qualsiasi logica condizionale. * Aggiungere una nuova trasformazione è facile: basta creare un nuovo bean che implementa Processor (e se si va per l'approccio con l'inserimento manuale in Processors , aggiungerlo all'elenco nel costruttore). * La classe Processors non ha bisogno di essere modificata quando aggiungi un nuovo processore.

    
risposta data 25.03.2018 - 19:24
fonte
2

Ciò che mi colpisce di questo è che stai parlando di più processori e di uno stato mutabile condiviso. Questa è raramente una buona combinazione.

Piuttosto, vedo un modo per selezionare gli utenti. Perché non usare un filtro?

Un metodo abbastanza leggibile è quello di farlo in pochi passi.

List<User> users = originalUsers;
for (processor : processors) {
    users = users
        .stream()
        .filter(
            u -> processor.isPassable(u)
        )
        .collect( 
            Collectors.toList() 
        )
    ;
}

Questo naturalmente invia tutte le raccolte intermedie al garbage collector, che va bene se hai finito con loro.

Puoi sicuramente annidare due stream qui, ma non aiuterà la leggibilità e sono riluttante a farlo senza test delle prestazioni che dimostrino che ne vale la pena.

    
risposta data 25.03.2018 - 20:38
fonte
0

È necessario disporre di classi che implementino una logica per determinare se è necessario aggiungere, aggiornare o eliminare un oggetto dall'elenco. Chiamiamoli le classi decision maker (DMC).

Queste classi dovrebbero contenere anche classi che eseguono ciascuna di queste operazioni (aggiungi, aggiorna o cancella). Chiamiamoli Enforcer Classes (ECs)

Ogni DMC dovrebbe avere un riferimento ad almeno una EC. Ora, puoi avere un DMC che ha tutte e tre le EC in qualche modo (tre attributi separati, raccolta, ecc.), O ne hai solo uno e poi assegnare dinamicamente quello che deve essere invocato. Quale approccio scegli dipende dalle specifiche dei tuoi requisiti.

Una volta stabilita questa gerarchia, è necessario disporre di una classe contenitore che deciderà quale dei DMC verrà utilizzato.

Nel caso in cui tutti debbano essere richiamati uno dopo l'altro, la soluzione suggerita da @MichalKosmulski è buona.

Se solo uno di questi ha bisogno di essere invocato, ma non sai quale, allora la catena di responsabilità è ciò di cui hai bisogno. Ogni DMC rappresenterebbe un collegamento nella catena. Ogni link dovrebbe verificare se dovrebbe essere invocato, e in caso affermativo, avrebbe perfom le azioni e si fermerà lì. In caso contrario, invocherà il prossimo collegamento nella catena.

Se solo uno di questi ha bisogno di essere invocato, e tu sai quale è prima del runtime, allora il modello di strategia è quello di cui hai bisogno, dove la strategia appropriata verrà istanziata sul runtime. In tal caso, ogni DMC sarebbe implementato come strategia.

    
risposta data 26.03.2018 - 11:15
fonte

Leggi altre domande sui tag