Il pattern design del comando è un buon modo per ridurre il numero di dipendenze nella mia classe?

7

Recentemente mi sono reso conto di avere troppe dipendenze in molte delle mie classi. Ora sto cercando di risolverlo per la classe più importante. Penso di avere un'idea di come, ma non sono sicuro se sia la migliore idea.

La classe più importante è una classe di servizio. Quello che fa è far sì che una "cosa" nell'app passi attraverso diversi stati. Esiste un flusso specifico che segue, attivato dagli utenti che eseguono determinate azioni nell'app. (Invia, conferma, rifiuta, chiudi, ...)

In questo momento, c'è un metodo pubblico per ogni azione che puoi fare per cambiare lo stato di quell'oggetto, circa 10 metodi. Questo sembra sbagliato, anche se la responsabilità di questa classe è "cambiare lo stato".

Quando lo stato cambia, tuttavia, è necessario che vengano eseguite altre azioni, come l'invio di una e-mail, la chiusura di determinate attività, la ricerca di documenti, ecc. Molte di queste attività hanno altre dipendenze e perché tutti i metodi sono nella stessa classe il servizio principale ora ha 12 dipendenze.

Il mio obiettivo principale è ridurre il numero di dipendenze nel servizio principale, pur contenendo una sorta di "panoramica" di tutte le azioni che puoi intraprendere.

Stavo guardando un elenco di modelli di progettazione comuni e ho notato il modello "Command". Penso di poter creare una serie di classi di comando come "SubmitThing", "CloseThing". Ognuno con le proprie dipendenze specifiche. In questo modo trasferisco le dipendenze ad altre classi e ogni azione ha le sue dipendenze.

Le informazioni che posso trovare sul pattern Command tuttavia descrivono i suoi principali vantaggi: è possibile tenere traccia di quali comandi sono stati eseguiti, ripristinarli, aggiungere la registrazione. Questo non è proprio il problema che volevo risolvere, anche se la descrizione del pattern sembra allettante.

Quindi, il modello di progettazione di Command è la strada da percorrere qui o ci sono schemi migliori per suddividere una classe che ha diverse azioni in parti più piccole?

Un piccolo esempio:

 
    public class ThingService {

        private IDocumentService documentService;
        private IMailService mailService;
        private IAnotherService anotherService;
        private IActionsOnClosingService actionsOnClosingService;

        public ThingService(IDocumentService documentService,
                            IMailService mailService,
                            IAnotherService anotherService,
                            ...
                            ) {
            this.documentService = documentService;
            this.mailService = mailService;
            this.anotherService = anotherService;
            ...
        }

        public void submitThing(Thing thing, SubmitData data) {
            thing.setStatus(SUBMITTED);
            thing.setData(data);
            documentService.doSomething();
            mailService.doSomething();
        }

        public void closeThing(Thing thing, CloseData data) {
            thing.setStatus(CLOSED);
            thing.setData(data);
            documentService.doSomething();
            mailService.doSomething();
            actionsOnClosingService.doActionsOnClosing();
        }
    }
    
posta geoffreydv 11.05.2016 - 17:20
fonte

2 risposte

4

Questo potrebbe essere risolto utilizzando la programmazione guidata dagli eventi e alcuni osservatori. Quante cose fa questo metodo?

public void submitThing(Thing thing, SubmitData data) {
    thing.setStatus(SUBMITTED);
    thing.setData(data);
    documentService.doSomething();
    mailService.doSomething();
}

Tre. Fa tre cose.

  1. Invia il thing .
  2. Ha qualcosa con un servizio di documentazione.
  3. Invia una notifica di posta elettronica che è stata inviata la thing .

Che cosa succede quando abbiamo bisogno di un'altra something per accadere quando viene inviato un thing ? Lo so! Modificheremo di nuovo questa classe!

Questo design viola sia i principi di Responsabilità Unica che i principi di Open-Closed. Confronta con questo progetto guidato dagli eventi. (Si prega di scusare il mio C # ...)

public event EventHandler<ThingSubmittedEventArgs> ThingSubmitted;

public void submitThing(Thing thing, SubmitData data) {
    thing.setStatus(SUBMITTED);
    thing.setData(data);

    var handler = ThingSubmitted;
    if (handler != null) {
        handler(this, data);
    }
}

Ora, quando dobbiamo fare ancora qualcos'altro, non dobbiamo modificare questa classe. Ascoltiamo solo l'evento ThingSubmitted e prendiamo qualsiasi azione appropriata per l'ascoltatore.

Come effetto collaterale vantaggioso di questo progetto, questa classe non ha più dipendenze su MailService o DocumentService .

    
risposta data 12.05.2016 - 01:22
fonte
2

So, is the Command design pattern the way to go here or are there better patterns for splitting up a class that has several actions into smaller pieces?

Dipenderà un po 'da quanto strettamente le azioni sono accoppiate l'una con l'altra.

Sulla possibilità è di combinare l'idea del modello di comando con quella di Composito. Ogni singola azione isolata è il suo comando. I comandi compositi, che sanno come ordinare l'invocazione dei loro figli, sono usati per organizzare il grafo dei comandi.

Le fabbriche, i generici fluenti e così via possono aiutarti a creare il grafico dei comandi senza un eccessivo accoppiamento diretto delle dipendenze.

Un'altra possibilità è un approccio guidato dagli eventi; quando un comando viene eseguito, pubblica un messaggio; gli abbonati a questi messaggi eseguono a loro volta i comandi che possono pubblicare nuovi eventi e le tartarughe fino in fondo.

Nella progettazione basata sul dominio, quest'ultima implementazione è molto comune. Ogni singolo comando ha come target un singolo aggregato, che è responsabile del mantenimento della propria invarianza. Quando l'aggregato cambia stato, trasmette eventi di dominio. I processori eventi downstream osservano tali eventi e inviano comandi aggiuntivi per guidare un processo.

Sulla via felice, dove tutto funziona, c'è poco da separare le scelte. Presta attenzione ai casi d'uso in cui i comandi iniziano a non funzionare - cosa dovrebbe succedere con i comandi precedenti se MailService non è disponibile? Udi Dahan ha tenuto una bella chiacchierata sulla messaggistica affidabile che discute alcune di queste preoccupazioni.

    
risposta data 11.05.2016 - 20:14
fonte