Divertente, questa domanda mi ha ricordato esattamente la stessa conversazione che ho avuto con uno dei nostri ingegneri sulla biblioteca di comunicazioni a cui stavo lavorando.
Invece dei comandi, avevo le classi Request e poi i RequestHandlers. Il design era molto simile a quello che stai descrivendo. Penso che parte della confusione che hai sia che vedi la parola inglese "comando" e pensi immediatamente "verbo, azione ... ecc".
Ma in questo disegno, pensa a Command (o Request) come a una lettera. O per chi non sa cosa sia un servizio postale, pensa all'e-mail. È semplicemente contenuto, disaccoppiato dal modo in cui tale contenuto dovrebbe essere interpretato.
Perché dovresti farlo? Nella maggior parte dei casi semplici, di Command Pattern non c'è motivo e si potrebbe fare in modo che questa classe esegua il lavoro direttamente. Tuttavia, fare il disaccoppiamento come nel tuo progetto ha senso se la tua azione / comando / richiesta deve percorrere una certa distanza. Ad esempio, attraverso, socket o pipe, o tra dominio e infrastruttura. O forse nella tua architettura i tuoi comandi devono essere persistenti (ad es. Il gestore dei comandi può eseguire 1 comando alla volta, a causa di alcuni eventi di sistema, arrivano 200 comandi e dopo i primi 40 processi viene arrestato). In tal caso, avendo una semplice classe di solo messaggio diventa molto semplice serializzare solo la parte del messaggio in JSON / XML / binary / qualunque e passarla lungo la pipeline fino a quando il suo gestore di comandi non è pronto per elaborarla.
Un altro vantaggio del disaccoppiamento di Command da CommandHandler è che ora hai l'opzione della gerarchia di ereditarietà parallela. Ad esempio, tutti i comandi potrebbero derivare da una classe di comandi di base che supporta la serializzazione. E forse hai 4 gestori di comandi su 20 che hanno molta somiglianza, ora puoi ricavarli dalla classe base del gestore venuto. Se avessi una gestione dei dati e dei comandi in una classe, questo tipo di relazione sarebbe rapidamente fuori controllo.
Un altro esempio per il disaccoppiamento sarebbe se il tuo comando richiedesse pochissimi input (ad es. 2 numeri interi e una stringa) eppure la sua logica di gestione fosse abbastanza complessa in cui vorresti archiviare i dati nelle variabili membro intermedie. Se si mettono in coda 50 comandi, non si desidera allocare memoria per tutta quella memoria intermedia, quindi si separa Command da CommandHandler. Ora accodate 50 strutture di dati leggere e l'archiviazione dei dati più complessa viene allocata una sola volta (o N volte se avete N gestori) da CommandHandler che sta elaborando i comandi.