Esiste qualche tecnica per aiutare a decidere dove si trova una determinata funzionalità?

3

Nella programmazione orientata agli oggetti, poiché utilizziamo classi e metodi in queste classi, quando implementeremo una determinata funzionalità, dobbiamo decidere dove appartiene , in altre parole, su quale classe appartiene .

Questo solleva domande come: "dovrei creare una nuova classe e aggiungerla qui" o "dovrei aggiungerla a una classe esistente".

Le cose principali di cui sono a conoscenza sono: (i) non esiste un modo giusto per farlo, ci sono molti modi possibili che finiscono per funzionare e (ii) implementare un modo e il refactoring successivo è normale.

In realtà, quando ho seguito lo sviluppo di ASP.NET Core su Github, ho visto alcune funzionalità spostarsi di tanto in tanto da qui a lì.

Il mio problema è il punto di partenza: implementerò una nuova funzionalità, conosco la logica che deve essere codificata, conosco le regole aziendali, non so dove collocarlo.

Conosco alcune teorie come SOLID e un po 'di DDD. Mi piace il DDD perché fornisce categorie di classi come "entità", "oggetti valore", "repository", "servizi di dominio", quindi sembra dare un punto di partenza.

La mia domanda è come usare tutto questo nella pratica. C'è qualche tecnica che aiuta a decidere dove (su quale classe) appartiene una determinata logica? Qual è il modo di pensare che dovrei adottare? La mia domanda è davvero questa: come dovrei pensare, qual è la linea di pensiero che devo seguire, per prendere questa decisione per iniziare (ovviamente non fare una scelta una volta per tutte, poiché questo può cambiare , ma almeno iniziare)?

    
posta user1620696 14.05.2017 - 22:29
fonte

4 risposte

3

Bene, questa è una domanda molto ampia, ed è una specie di arte, ma qui ci sono alcune aree di pensiero che possono aiutarti, in ordine dal più comune / importante al non comune.

Segui gli schemi di progettazione l'applicazione presenta uno schema di progettazione esistente che disciplina il posizionamento di questa funzionalità? Ad esempio, in un sito MVC, i metodi di azione appartengono ovviamente a un controller. In un'applicazione a più livelli, i formattatori di testo appartengono al livello di presentazione; l'accesso ai dati appartiene a un livello di accesso ai dati. Se la tua funzione crea oggetti, forse appartiene a una classe factory. Ecc.

Riduci l'ambito. Di quali dati ha bisogno la funzione? Se la funzione opera su dati contenuti in un oggetto esistente, forse appartiene a quell'oggetto in modo che tu possa mantenere le tue variabili private / strettamente a scope.

Riduci al minimo le dipendenze delle chiamate. Le dipendenze sono la rovina del software complesso. Se riesci a renderlo privato se lo metti in un posto ma sei costretto a renderlo pubblico o statico da qualche altra parte, vai sul percorso privato. Al contrario, se trovi che devi cambiare altre funzioni da privato a pubblico per far funzionare la tua nuova funzione, probabilmente è nel posto sbagliato.

Ha senso per gli altri? Se qualche altro sviluppatore ha bisogno di questa funzione, ma non sa dove si trova, dove è probabile che guardi prima? Per esempio. se si tratta di una funzione di supporto statico, potrebbe appartenere a una classe statica denominata Helpers. Se funziona su un oggetto runtime di sistema, forse dovrebbe essere scritto come un metodo di estensione. Rendi la scelta ragionevole e ti ringrazieranno

Ciclo di rilascio. Se è probabile che la funzione cambi spesso, probabilmente non dovrebbe entrare in una libreria principale condivisa tra diversi progetti con un ciclo di vita delle versioni lente. Al contrario, se è un codice molto stabile, probabilmente non ha bisogno di essere racchiuso in un'architettura collegabile.

Licenza o IP. Se la funzione comprende una preziosa proprietà intellettuale, potresti volerla individuare in una libreria separata che può essere venduta separatamente.

Sicurezza. Se la funzionalità è sensibile, puoi separarla in modo da poter utilizzare le autorizzazioni di accesso al codice o metterla su un server diverso.

    
risposta data 17.05.2017 - 03:47
fonte
1

Questa è una domanda abbastanza ampia, ma il principio fondamentale che dovresti tenere a mente è questo. Dovresti avere classi che singolarmente fanno relativamente poco, sono molto ben incapsulate, implementano le proprietà correttamente e sono generalmente progettate nel modo più semplice possibile. Questo significa che potresti finire con molte classi, e questo è perfettamente ok .

Ciò significa che se hai una nuova funzionalità e non è immediatamente ovvio che appartiene a una classe esistente, allora probabilmente non e dovresti provare a pensare ai modi per rendere tale funzionalità indipendente e molto liberamente accoppiata al resto del sistema. Ciò rende molto più facile il refactoring più avanti, se mai ne hai bisogno.

I tuoi accoppiamenti stretti dovrebbero essere intra-classe e i tuoi accoppiamenti liberi dovrebbero essere inter-class. Lavorate sempre per questo e non andrete molto lontano.

    
risposta data 20.08.2017 - 01:06
fonte
0

In generale, dovresti cercare di ridurre al minimo la quantità totale di codice, massimizzando il riutilizzo e massimizzando l'incapsulamento allo stesso tempo. È difficile dare risposte generali e, se si utilizza solo una serie di "principi", è possibile concludere con soluzioni generiche, non specifiche per il problema. La soluzione migliore per ciascuna di queste attività, aggiungendo una funzione, sarà specifica per la funzione.

Ad esempio, se mi venisse chiesto di aggiungere "annulla e ripristina" a un'applicazione esistente, se l'applicazione è stata progettata sin dall'inizio per supportare annulla e ripristina, non sarebbe difficile. Ma una cosa del genere di solito non è pianificata in anticipo, e l'aggiunta della feature diventa complessa. Undo e redo possono essere implementati in molti modi. Uno potrebbe essere una pila di istantanee dello stato del modello. Un altro potrebbe essere una pila di modifiche di singole proprietà allo stato del modello. In entrambi i casi, la funzionalità di annullamento e ripetizione può essere centralizzata, ma una soluzione (istantanee) è meno invasiva per la progettazione dell'applicazione.

    
risposta data 17.05.2017 - 02:31
fonte
0

Se non puoi decidere ora, forse YAGNI . Frank è un buon caso per ridurre la complessità fondamentale. Se a questo punto si adatterà logicamente a una classe esistente, prosegui con quella. In questo modo, in genere, sarà più semplice risolvere i problemi quando i problemi arriveranno più tardi, piuttosto che avere un'architettura complessa all'inizio che potrebbe rivelarsi più difficile da implementare e eseguire il debug.

Puoi sempre refactoring e creare nuove classi più tardi quando ci sono ragioni più convincenti per farlo (ad es. è richiesta un'altra logica aziendale, che sembra correlata a quella attuale ed entrambe sembrano appartenere a una classe e condividi le variabili), specialmente se scrivi la logica in modo che non sia strettamente accoppiata alla classe esistente.

    
risposta data 17.05.2017 - 02:56
fonte