Come definisci una responsabilità?

7

Ho imparato i principi SOLID nel 2011 e cerco di usarli nel mio lavoro quotidiano.

Tuttavia, spesso mi trovo a vagare se infrango il principio della Responsabilità Unica. Una classe può avere una serie di responsabilità:

  1. Presentazione
  2. Repository
  3. servizio
  4. Dominio
  5. Infrastruttura ecc.

Vedo anche lamentele se una classe contiene un comportamento che dovrebbe essere in un altro, ad es. Funzionalità OrderItem nella classe dell'ordine. Questo è quello con cui lotto.

Non mescolo la presentazione con l'accesso ai dati. Non mischio il servizio con il dominio ecc. Tuttavia, vedo le domande qui a mio agio e un rispondente si lamenta che l'SRP è rotto. Come decidi che una classe ha una singola responsabilità? Mi rendo conto che questo può variare da un dominio all'altro. Quello che sto cercando è una metodologia che mi aiuti a decidere se una classe ha una sola responsabilità (forse una serie di domande).

    
posta w0051977 27.06.2017 - 21:15
fonte

5 risposte

15

Quando qualcosa ha una sola responsabilità, dovrebbe avere solo una ragione per cambiare.

In effetti questo è un grande motivo per cui ci preoccupiamo di dare alle cose una sola responsabilità. Contiene l'impatto delle modifiche.

Questo non significa che all'interno di ogni classe trovi solo un metodo o una variabile membro. Significa che i metodi all'interno dovrebbero ciascuno isolare il cambiamento gli uni dagli altri avendo le proprie responsabilità. E la classe nel suo insieme dovrebbe avere una responsabilità che li comprenda, e solo loro. Oh, e la classe dovrebbe avere un nome che renda i metodi che trovi non sorprende affatto.

Quindi sì, se volessi potresti organizzare una singola classe che aveva un metodo responsabile per la presentazione, uno per il repository, uno per fornire un servizio, uno che forniva alcune funzionalità di dominio e uno che esisteva per soddisfare alcuni requisiti di infrastruttura . Sarebbe strano Ma fintanto che la classe generale è esistita per soddisfare una singola responsabilità, come per esempio CatVideo , allora tutti questi metodi dovrebbero fare meglio di tutte queste cose per un CatVideo .

In breve, le responsabilità possono nidificare. Va bene guardare dentro qualcosa con una sola responsabilità e trovare altre cose che hanno responsabilità singole.

Ciò che non va bene è che una classe sta cercando di fare il lavoro di due classi. O una funzione che cerca di svolgere due funzioni. O un pacchetto che sta cercando di fare il lavoro di due pacchetti. Qualunque sia la cosa del momento, dicendomi di cosa è responsabile non dovrebbe coinvolgere la parola "e".

Ora se alcuni dei metodi in CatVideo riguardano Cat e alcuni in Video, potrebbe essere che invece di una singola responsabilità si comprimano due responsabilità e in realtà occorrono due oggetti: Cat e Video . Se queste responsabilità possono essere messe in disparte, dovrebbero esserlo. Ma se CatVideo è davvero una singola idea in questo dominio, allora va bene così com'è.

Quando le responsabilità sono nidificate bene, puoi strapparne una senza impattare sugli altri. Se la tua classe CatVideo ha il suo repository modificato, ciò non dovrebbe significare che la presentazione debba cambiare. Se la presentazione ha assunto alcune responsabilità sui repository.

SRP non riguarda solo le classi. Si applica a ogni astrazione che hai nella tua lingua.

    
risposta data 27.06.2017 - 21:45
fonte
7

Il Single Responsibility Principle (SRP) è ampiamente frainteso, probabilmente a causa di un nome fuorviante.

Il principio riguarda il modo in cui il codice cambia. Non nel senso di cambiamenti di stato durante l'esecuzione, ma su come il codice sorgente viene modificato nel tempo in risposta ai mutevoli requisiti.

Il peggior tipo di sistema è quello in cui un piccolo requisito cambia richiede la modifica del codice in numerosi punti diversi nella sorgente e dove ogni cambiamento causa l'insorgere di bug e comportamenti imprevisti in parti del codice apparentemente non correlate.

È facile scrivere codice da zero. La principale sfida del design del software è scrivere codice che possa cambiare e adattarsi nel tempo. Se guardi un codice e decidi di riscriverlo da zero per supportare alcune nuove funzionalità, il codice non è stato progettato in base all'SRP.

L'SRP ti dice di pensare a cosa potrebbe causare la modifica del codice nel tempo. Ad esempio: perché la presentazione è separata dal dominio? Perché è probabile che l'interfaccia utente di un'applicazione cambierà indipendentemente dalla logica del dominio. Succede tutto il tempo. Quindi dividi il codice in unità e classi in base a cosa può cambiare in modo indipendente.

Quindi dimentica le "responsabilità" - la parola è troppo nebulosa da sola. Pensa a quali modifiche ai requisiti potrebbero rendere necessario modificare un oggetto.

    
risposta data 28.06.2017 - 10:15
fonte
4

Questa è una buona domanda.

Se mi è concesso di parlare, direi che la lingua è la chiave per la modellazione. La metodologia che stai cercando è nascosta nella tua comprensione della lettura.

Se l'OOP è pensato per essere un paradigma per modellare il mondo reale, una buona domanda sarebbe: Qual è il mondo reale?

Il mondo reale non è nient'altro che la nostra percezione di questo. E tu indovina cosa? La nostra percezione è modellata dal nostro linguaggio. Di conseguenza, per la modellazione, dobbiamo concentrarci sulla comprensione della lettura e fare attenzione al linguaggio che usiamo per descrivere il problema (il dominio).

Prestando attenzione al linguaggio possiamo supporre che le diverse astrazioni che entrano in gioco e il modo in cui collaborano tra loro. Non sto dicendo nulla di nuovo, immagino tu sia a conoscenza della così famosa lingua onnipresente di Eric Evans.

Per me, le astrazioni e le responsabilità sono correlate.

A questo punto potresti essere interessato a GRASP .

Possiamo ripetere l'analisi della lingua per ognuna delle astrazioni perché ognuna di esse può essere scomposta in diversi livelli di astrazione . Di quanti livelli abbiamo bisogno varia da progetto a progetto, ma di solito, una regola empirica è KISS.

In short, responsibilities can nest. It's OK to look inside something with a single responsibility and find other things that have single responsibilities.

CandiedOrange' answer

Per quanto riguarda questo processo (composizione / scomposizione) potrebbe interessarti top-down e in basso -up strategie di progettazione.

Prendi come esempio una catena di fornitura , dove fornire è il livello più alto di astrazione, mentre i diversi lavori lungo tutta la catena sono livelli più bassi di astrazione.

Top-level : Supply

Mid-level : Assembly -> Packaging -> Shipping -> Delivery

...

Se abbiamo bisogno di migliorare la nostra catena con un processo di consegna più veloce, possiamo affrontare la soluzione cambiando solo questa responsabilità senza dover cambiare l'intera catena. Ma possiamo farlo perché capiamo che consegna e spedizione sono responsabilità diverse.

When something has only one responsibility it should only have one reason to change.

CandiedOrange' answer

Hai bisogno di un'altra prova dell'importanza della lingua?

Oh and the class should have a name that makes the methods you find inside no surprise at all

CandiedOrange' answer

Se puoi fornire le tue astrazioni con nomi coerenti o se i nomi delle tue astrazioni possono descrivere in modo coerente ciò che ognuno di loro dovrebbe fare, è probabile che tu abbia modellato le responsabilità.

    
risposta data 28.06.2017 - 00:53
fonte
0

Per rimanere fedeli al tuo esempio di ordine, una volta che hai riconosciuto il tuo ordine raggruppa una serie di articoli, significa che la parte di raggruppamento è di sua responsabilità e dovresti fare attenzione con l'aggiunta di funzionalità che non sono correlate al raggruppamento di elementi.

Quindi non vorrai che il tuo ordine offra un prezzo totale. Questo non è correlato al mantenimento della raccolta articoli dell'ordine, è un problema di fatturazione. Né vuoi che l'ordine di capire se tutti gli articoli sono in magazzino, questo è un problema di magazzino. Vuoi che offra un modo per enumerare gli elementi in modo che altre classi possano scorrere attraverso di essi e recuperare le informazioni ad essi relative. E tu vuoi limitare il numero massimo di oggetti che dovrebbe consentire per un singolo ordine, se questo dovesse essere limitato. Aggiunta di articoli, rimozione di articoli, questi sono responsabilità fondamentali. Idealmente l'ordine non dovrebbe sapere cosa è un articolo dell'ordine o cosa ha da offrire.

Quindi cosa rende l'ordine più di una lista, allora? È correlato a un cliente. Ha una data e un'ora per il posizionamento e la cancellazione. Queste sono proprietà native, si riferiscono direttamente all'ordine stesso. Non essere tentato di inserire tutti i tipi di informazioni sull'articolo dell'ordine consolidato nell'interfaccia dell'ordine.

Allora come calcoleresti quel prezzo totale? Potresti avere una classe di fattura che accetta un ordine come argomento per il suo costruttore. L'importo da pagare per l'ordine sarebbe un valore appropriato per la fattura perché si tratta di denaro che deve essere pagato dal cliente.

Per la disponibilità degli articoli, potresti avere una classe di azioni che ha un metodo GetItemAvailabily, che accetta un articolo di ordine come argomento. Una classe OrderInformationProducer può prendere un ordine, alimentare i suoi articoli a Stock.GetAvailability e produrre un oggetto di informazioni sugli ordini che comunichi al cliente cosa può aspettarsi.

L'esempio sopra riportato è solo per illustrare una rigorosa separazione delle preoccupazioni. Non ho mai creato un sistema di gestione degli ordini, quindi potrebbe essere troppo semplice o poco pratico. Spero che trasmetta l'idea di SRP.

    
risposta data 27.06.2017 - 22:49
fonte
-3

I principi SOLID sono un buon punto di partenza per considerare come progettare l'architettura di un sistema, ma contiene un insieme completo di tutti i principi che dovrebbero essere valutati da un architetto del software.

Proprio come un esempio, SOLID non include il principio di Separation of Concerns (SoC), che è un principio progettuale piuttosto importante, e il principio SRP in SOLID non contiene né sostituisce il principio SoC.

Il principio SoC ci dà molti suggerimenti su come organizzare il codice in diversi livelli software e quali tipi di responsabilità dovrebbero essere raggruppati insieme in quale livello.

Pertanto, è solo al livello della logica aziendale in cui deve risiedere il codice della logica aziendale: questo è l'unico livello in cui gli oggetti business o gli oggetti dominio esistono realmente e hanno senso.

A livello di presentazione, solo la logica di presentazione dovrebbe risiedere.

Al livello di accesso ai dati, dovrebbe risiedere solo la logica di accesso ai dati di base: qui è dove deve avvenire la mappatura tra il modello di oggetto e il modello di dati.

Ma abbiamo bisogno di un mezzo per trasferire lo stato degli oggetti di business tra i livelli: questo è ciò che fanno gli oggetti di trasferimento dati (DTO)!

In un certo senso, le DTO sono come una preoccupazione trasversale, perché sono presenti in tutti i livelli: le DTO non sono oggetti di business o oggetti di dominio, poiché la loro unica responsabilità (SRP) è di trasferire lo stato degli oggetti di dominio al resto dei livelli.

Riguardo ai tuoi dubbi sulla classe Order e la classe OrderItem, devi capire come funzionano gli oggetti compositi: ogni istanza data della classe OrderItem non ha motivo di esistere al di fuori di un'istanza della classe Order, che è una classe composita, e per lo stesso token, qualsiasi istanza data della classe Order non potrebbe esistere senza una proprietà collection di OrderItems, quindi, in realtà, non c'è spazio per la confusione: esistono solo come classi separate solo nel codice sorgente, ma hanno nessun significato reale come classi separate in runtime.

Spero che questa piccola introduzione sia abbastanza utile per rispondere alle tue domande e ai tuoi dubbi.

Cordiali saluti, GEN

    
risposta data 27.06.2017 - 21:51
fonte

Leggi altre domande sui tag