Devo scendere a compromessi: DRY o Command-Query-Separation?


10

Recentemente ho refactoring un metodo che era sia un comando che un metodo di query.

Dopo averlo separato in un metodo a un comando e un metodo di query, ho scoperto che ora ci sono più posizioni nel codice in cui sto chiamando il comando per ottenere il valore dalla query, che sembra una violazione del principio DRY.

Ma se dovessi racchiudere quel codice comune in un metodo, quel metodo sarebbe sia il comando che una query. È accettabile?


ok, non sapevo se la comunità fosse d'accordo, e non sono riuscito a trovare alcuna discussione su questo argomento.
Kris Welsh,

È più comunemente chiamato CQRS google.com.au/…
Daniel Little

@DanielLittle - no non lo è. CQS e CQRS sono soggetti nettamente diversi. CQRS è un modello architettonico molto più coinvolto mentre CQS è più un modello progettuale e molto più facile da comprendere e implementare. Vedi codebetter.com/gregyoung/2009/08/13/command-query-separation
Erik

@Erik Funkenbusch Hai ragione
Daniel Little,

Risposte:


11

Ci sono sempre compromessi da considerare tra principi progettuali contrastanti. Il modo per risolverlo è quello di esaminare le ragioni alla base dei principi. In questo caso, non essere in grado di eseguire una query senza eseguire il comando è problematico, ma essere in grado di eseguire un comando senza eseguire la query è generalmente innocuo. Finché esiste un modo per eseguire la query autonomamente, non vedo alcun motivo per non aggiungere il risultato della query al comando, soprattutto se fatto qualcosa del genere:

QueryResult command()
{
   // do command stuff
   return query();
}

4

Non ho mai sentito parlare di Command-Query-Separation (CQS) prima, ma sembra che si riferirebbe al Single Responsibility Principle (SRP), che afferma che una funzione / classe dovrebbe idealmente essere responsabile di fare una cosa e una cosa sola .

Se il tuo codice di comando è di 20 righe di codice e il codice di query è di altre 30 righe e sono tutte in un corpo di funzione, chiaramente stai violando SRP e suppongo che anche CQS e quei due pezzi di logica dovrebbero essere separati l'uno dall'altro .

Tuttavia, andando con il tuo esempio ipotetico, molto probabilmente creerei un metodo wrapper che combina il comando e la query in modo che DRY non venga violato in numerosi punti del codice. Inoltre, non lo considero una violazione di SRP (e forse CQS), poiché il wrapper ha ancora una sola responsabilità: combinare il comando con una query e creare un'astrazione di livello superiore più facile da consumare.

Penso che il metodo wrapper sia una soluzione perfettamente accettabile e per illustrarlo, facciamo un passo avanti nel tuo esempio. E se fosse necessario eseguire 2 query anziché 1 e quindi eseguire un'azione di comando basata su quella. Quindi le tue 2 righe di codice sarebbero 6 o 8. E se ci fosse qualche validazione / verifica dei dati tra l'una e l'altra, quindi ora hai 15 righe di codice. Ci penseresti due volte sulla creazione di un wrapper che fa tutto questo, piuttosto che cospargere quelle 15 righe in più file?


Penso che il "principio unico" di un wrapper dovrebbe essere quello di mantenere DRY gli altri metodi che richiedono il comando e la query.
Droogans,


Mentre la soluzione di Karl a questo problema è migliore, trovo che la tua elaborazione su funzioni wrapper più lunghe sia un ottimo punto.
Kris Welsh,

-3

L'ASCIUGATURA è più importante, poiché risolve un'esigenza molto più fondamentale, evitando sforzi ridondanti ed effettivamente sprecati. Questa è una cosa fondamentale: non è necessario essere programmatori per capirlo.

CQS è una risposta alla difficoltà, in lingue senza supporto per gli effetti di tracciamento, di comprendere il codice che viene eseguito sia per i suoi risultati che per i suoi effetti. Però:

  1. La necessità di eseguire il codice per i suoi risultati non può essere evitata, poiché questa è la base per comporre grandi programmi da piccole unità.

  2. La necessità di eseguire il codice per i suoi effetti non può essere evitata, perché, al di fuori della matematica e dell'informatica teorica, il valore dell'esecuzione di un programma risiede su ciò che osservabilmente può fare per noi.

  3. La necessità di causare effetti e produrre risultati nello stesso codice non può essere evitata, perché, in pratica, abbiamo bisogno sia di effetti che di composizionalità, non solo l'uno o l'altro.

La vera soluzione al problema di tenere traccia degli effetti troppo difficili per gli esseri umani senza aiuto è, ovviamente, che i computer ci aiutino ! Una cosa simile si può dire sul tracciamento di relazioni complesse tra i valori di runtime (come la validità degli indici di array), per i quali eccezioni e contratti applicati dal runtime costituiscono (non) soluzioni.

In conclusione, "soluzioni" come CQS si limitano semplicemente a progettare programmi secondo solidi principi basati sulla realtà. Vai a SECCO.


A volte è necessario evitare l'accoppiamento per ridurre la complessità. Dovresti dare un'occhiata a CQRS.
Daniel Little

@Lavinski: lo strumento migliore per evitare la complessità (non ridurla, cioè inutile) è l'astrazione - disaccoppiando l'essenza generica dei problemi che stiamo risolvendo dai particolari dettagli delle istanze di detti problemi generici. Ricette magiche (o "schemi di progettazione" come sento che vengono chiamati) nella migliore delle ipotesi possono impedirti di causare troppi danni quando sbagli il tuo disegno, ma non possono trasformare un disegno sbagliato in quello giusto.
pione

@Lavinski: Rispetto specificamente a CQRS, la soluzione alternativa concettualmente corretta è 1. comprendere il modello di dati (nessuna quantità di livelli oggetto può eliminare la necessità per questo), 2. codificare quante più proprietà di correttezza possibile nello schema del database. (Purtroppo, i RDBMS più popolari forniscono un supporto piuttosto limitato per questi ultimi, per non parlare di quelli NoSQL, che lo rendono ancora più sbagliato. La mia ricerca attuale sta fornendo una soluzione migliore per questo.)
pione

CQRS funziona completamente in linea con Domain Driven Design. Ti suggerisco di fare un po 'di ricerca. Il dominio all'interno dell'applicazione dovrebbe imporre la correttezza non l'archivio dati.
Daniel Little

1
@ EduardoLeón: se vuoi dimostrare che il tuo progetto è corretto, prova a scrivere dei test per il tuo programma. Posso garantirvi che il lancio di CQS ostacolerà solo i vostri sforzi in tal senso.
Stefan Billiet,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.