SOLID vs. Evitare l'astrazione prematura

27

Capisco che SOLID dovrebbe realizzarlo e utilizzarlo regolarmente in situazioni in cui la modularità è importante e i suoi obiettivi sono chiaramente utili. Tuttavia, due cose mi impediscono di applicarlo in modo coerente attraverso il mio codebase:

  • Voglio evitare l'astrazione prematura. Nella mia esperienza, disegnare linee di astrazione senza casi d'uso concreti (il tipo che esiste ora o nel futuro prevedibile ) porta a essere disegnate nei posti sbagliati. Quando provo a modificare questo codice, le linee di astrazione si intromettono invece che aiutare. Pertanto, tendo ad errare sul lato di non disegnare alcuna linea di astrazione fino a quando non ho una buona idea di dove sarebbero utili.

  • Trovo difficile giustificare l'aumento della modularità fine a se stessa, se rende il mio codice più dettagliato, più difficile da capire, ecc. e non elimina alcuna duplicazione. Trovo che il codice procedurale semplice o strettamente accoppiato o il codice oggetto di Dio sia a volte più facile da capire rispetto al codice ravioli molto ben fatto perché il flusso è semplice e lineare. È anche molto più facile da scrivere.

D'altra parte, questa mentalità spesso porta a oggetti di Dio. In generale, li rifatto in modo conservativo, aggiungendo chiare linee di astrazione solo quando vedo emergere chiari schemi. Che cosa, se non altro, è sbagliato con gli oggetti di Dio e il codice strettamente accoppiato se non hai chiaramente bisogno di più modularità, non hai una duplicazione significativa e il codice è leggibile?

EDIT: Per quanto riguarda i singoli principi SOLID, intendevo sottolineare che Liskov Sostituzione è IMHO una formalizzazione del buon senso e dovrebbe essere applicata ovunque, poiché le astrazioni non hanno senso se non lo sono. Inoltre, ogni classe dovrebbe avere una singola responsabilità a un certo livello di astrazione, sebbene possa essere un livello molto alto con i dettagli di implementazione stipati in un'unica enorme classe di 2000 linee. Fondamentalmente, le tue astrazioni dovrebbero avere un senso in cui scegli di astrarre. I principi che pongo nei casi in cui la modularità non è chiaramente utile sono la segregazione open-closed, la segregazione dell'interfaccia e in particolare l'inversione di dipendenza, dal momento che si tratta di modularità, non solo di avere astrazioni sensate.

    
posta dsimcha 06.04.2011 - 20:16
fonte

12 risposte

8

I seguenti sono semplici principi che puoi applicare per aiutarti a capire come bilanciare la progettazione del tuo sistema:

  • Principio della singola responsabilità: S in SOLID. Puoi avere un oggetto molto grande in termini di numero di metodi o quantità di dati e continuare a mantenere questo principio. Prendi ad esempio l'oggetto String Ruby. Questo oggetto ha più metodi di quanti ne puoi scuotere, ma ha ancora una sola responsabilità: tenere una stringa di testo per l'applicazione. Non appena il tuo oggetto inizia ad assumere una nuova responsabilità, inizia a pensarci seriamente. Il problema della manutenzione è chiedersi "dove mi aspetterei di trovare questo codice se avessi dei problemi in seguito?"
  • La semplicità conta: Albert Einstein ha dichiarato: "Rendi tutto il più semplice possibile, ma non più semplice". Hai davvero bisogno di questo nuovo metodo? Riuscirai a realizzare ciò che ti serve con le funzionalità esistenti? Se pensi davvero che debba esserci un nuovo metodo, puoi cambiare un metodo esistente per soddisfare tutto ciò di cui hai bisogno? Essenzialmente refactoring mentre costruisci nuove cose.

In sostanza stai cercando di fare il possibile per non spararti ai piedi quando arriva il momento di mantenere il software. Se i tuoi oggetti di grandi dimensioni sono astrazioni ragionevoli, ci sono pochi motivi per dividerli solo perché qualcuno ha trovato una metrica che dice che una classe non dovrebbe essere più di X linee / metodi / proprietà / ecc. Se mai, queste sono linee guida e non regole rigide e veloci.

    
risposta data 06.04.2011 - 21:06
fonte
5

Penso che per la maggior parte hai risposto alla tua stessa domanda. SOLID è un insieme di linee guida su come refactoring il tuo codice, quando i concetti devono essere spostati su livelli di astrazioni.

Come tutte le altre discipline creative non ci sono assoluti - solo compromessi. Più lo fai, più diventa "facile" decidere quando è sufficiente per il tuo attuale dominio o i tuoi requisiti.

Detto questo - l'astrazione è il cuore dello sviluppo del software - quindi se non c'è una buona ragione per non farlo, allora la pratica ti renderà migliore, e avrai un istinto per i compromessi. Quindi, favoriscilo a meno che non diventi dannoso.

    
risposta data 06.04.2011 - 20:43
fonte
5
I want to avoid premature abstraction.In my experience drawing abstraction lines without concrete use cases... 

Questo è parzialmente giusto e parzialmente sbagliato.

Wrong
Questo non è un motivo per impedirne l'applicazione dei principi OO / SOLID. Impedisce solo di applicarli troppo presto.

Che è ...

Giusto
Non effettuare il refactoring del codice fino a quando non viene rideterminato; quando è completo. Oppure, quando i "casi d'uso" sono tutti lì come dici tu.

I find simple, tightly coupled procedural or God object code is sometimes easier to understand than very well-factored...

Quando lavori da solo o in un silo
Il problema con il non fare OO non è immediatamente ovvio. Dopo aver scritto la prima versione di un programma:

Code is fresh in your head
Code is familiar
Code is easy to mentally compartmentalize
You wrote it

3/4 di queste cose buone muoiono rapidamente in breve tempo.

Infine, riconosci che hai oggetti di Dio (oggetti con molte funzioni), quindi riconosci le singole funzioni. Incapsula. Mettili in cartelle. Usa le interfacce una volta ogni tanto. Fatti un favore per la manutenzione a lungo termine. Soprattutto perché i metodi non refactored tendono a gonfiarsi infinitamente e diventare metodi di Dio.

In un team
I problemi con il non fare OO sono immediatamente evidenti. Un buon codice OO è almeno in qualche modo auto-documentabile e leggibile. Soprattutto se combinato con un buon codice verde. Inoltre, la mancanza di struttura rende difficile separare le attività e l'integrazione è molto più complessa.

    
risposta data 06.04.2011 - 21:53
fonte
3

Non c'è niente di sbagliato in oggetti di grandi dimensioni e codice strettamente accoppiato quando sono appropriati e quando non è richiesto niente di meglio progettato . Questa è solo un'altra manifestazione di una regola pratica che si trasforma in dogma.

L'accoppiamento lento e gli oggetti piccoli e semplici tendono a fornire vantaggi specifici in molti casi comuni, quindi è una buona idea utilizzarli, in generale. Il problema sta nelle persone che non capiscono la logica dietro i principi che cercano ciecamente di applicarle universalmente, anche dove non si applicano.

    
risposta data 06.04.2011 - 20:33
fonte
2

Tendo ad avvicinarmi di più al punto di vista di Non averne bisogno. Questo post si concentrerà su un elemento in particolare: l'ereditarietà.

Nel mio progetto iniziale creo una gerarchia di cose che so che ce ne saranno due o più. È probabile che abbiano bisogno di molto dello stesso codice, quindi vale la pena progettarlo dall'inizio. Dopo che il codice iniziale è a posto , e ho bisogno di aggiungere più funzionalità / funzionalità, guardo cosa ho e penso, "Qualcosa ho già implementato la stessa funzione o simile?" Se è così, è più probabile che una nuova astrazione implorasse di essere rilasciata.

Un buon esempio di questo è un framework MVC. Partendo da zero, crei una grande pagina con un codice nascosto. Ma poi vuoi aggiungere un'altra pagina. Tuttavia, l'unico codice dietro implementa già molta della logica richiesta dalla nuova pagina. Quindi, estrai una Controller classe / interfaccia che implementerà la logica specifica per quella pagina, lasciando le cose comuni nel codice originale "dio".

    
risposta data 06.04.2011 - 21:19
fonte
1

Se sai come sviluppatore quando NON applicare i principi a causa del futuro del codice, allora fa bene a te.

Spero che SOLID rimanga nella parte posteriore della tua mente per sapere quando è necessario astrarre le cose per evitare la complessità e migliorare la qualità del codice se inizia a degradarsi nei valori che hai indicato.

Ancora più importante, però, considera che la maggior parte degli sviluppatori sta facendo il lavoro diurno e non ti interessa tanto quanto te. Se inizialmente si impostano i metodi a lunga esecuzione, quali sono gli altri sviluppatori che entrano nel codice quando pensano di mantenerlo o di estenderlo? ... Sì, avete indovinato, funzioni BIGGER, classi di esecuzione più lunghe e ALTRO oggetti di Dio, e non hanno in mente i principi per essere in grado di catturare il codice in crescita e incapsularlo correttamente. Il codice "leggibile" ora diventa un pasticcio in decomposizione.

Quindi potresti obiettare che un cattivo sviluppatore lo farà a prescindere, ma almeno hai un vantaggio migliore se le cose sono mantenute semplici e ben organizzate dall'inizio.

Non mi interessa particolarmente una "mappa del registro" globale o un "oggetto divino" se sono semplici. Dipende molto dal design del sistema, a volte puoi farla franca e rimanere semplice, a volte non puoi.

Non dimentichiamo inoltre che le grandi funzioni e gli oggetti di Dio possono rivelarsi estremamente difficili da testare. Se vuoi rimanere Agile e sentirti "sicuro" quando ri-factoring e modifica del codice, hai bisogno di test. I test sono difficili da scrivere quando funzioni o oggetti fanno molte cose.

    
risposta data 06.04.2011 - 20:40
fonte
1

Il problema con un oggetto divino è che di solito puoi rompere in pezzi proficuamente. Per definizione, fa più di una cosa. L'intero scopo di dividerlo in classi più piccole è che puoi avere una classe che fa bene una cosa (e non dovresti neanche estendere la definizione di "una cosa"). Ciò significa che, per ogni classe, una volta che conosci l'unica cosa che dovrebbe fare, dovresti essere in grado di leggerlo abbastanza facilmente e dire se sta facendo quella cosa correttamente.

Penso che sia che abbia troppa modularità e troppa flessibilità, ma si tratta più di un problema di progettazione eccessiva e di sovradimensionamento, in cui stai tenendo conto dei requisiti che non sai nemmeno che il cliente vuole. Molte idee di design sono orientate a rendere facile il controllo delle modifiche al modo in cui viene eseguito il codice, ma se nessuno si aspetta la modifica, è inutile includere la flessibilità.

    
risposta data 06.04.2011 - 20:44
fonte
1

Uso una linea guida più semplice: se riesci a scrivere test unitari e non hai duplicazioni di codice, è abbastanza astratto. Inoltre, sei in una buona posizione per il successivo refactoring.

Oltre a questo, dovresti tenere a mente SOLID, ma più come linea guida che come regola.

    
risposta data 06.04.2011 - 22:33
fonte
0

What, if anything, is wrong with God objects and tightly coupled code if you don't clearly need more modularity, don't have significant duplication and the code is readable?

Se l'applicazione è abbastanza piccola, tutto è manutenibile. Nelle applicazioni più grandi, gli oggetti di Dio diventano rapidamente un problema. Alla fine, l'implementazione di una nuova funzione richiede la modifica di 17 oggetti di Dio. Le persone non fanno molto bene seguendo le procedure in 17 fasi. Gli oggetti di Dio vengono costantemente modificati da più sviluppatori e tali modifiche devono essere unite ripetutamente. Non vuoi andare lì.

    
risposta data 07.04.2011 - 05:25
fonte
0

Condivido le tue preoccupazioni sull'astrazione eccessiva e inappropriata, ma non sono necessariamente così preoccupato per l'astrazione prematura.

Probabilmente sembra una contraddizione, ma è improbabile che l'uso di un'astrazione crei un problema, a patto che non lo si usi troppo presto - cioè finché si è disposti e in grado di rifattorici se necessario.

Ciò che questo implica è un certo grado di prototipazione, sperimentazione e backtracking nel codice.

Detto questo, non c'è una semplice regola deterministica da seguire che risolva tutti i problemi. L'esperienza conta molto, ma devi ottenere quell'esperienza facendo errori. E ci sono sempre più errori da fare per cui gli errori precedenti non ti avrebbero preparato.

Anche così, considera i principi del libro di testo come punto di partenza, quindi impara a programmare i lotti e vedere come funzionano questi principi. Se fosse possibile fornire linee guida migliori, più precise e affidabili di cose come SOLID, qualcuno probabilmente lo avrebbe fatto ora - e anche se lo fossero, quei principi migliorati sarebbero ancora imperfetti, e la gente si chiederebbe quali siano i limiti in questi .

Buon lavoro anche - se qualcuno potesse fornire un algoritmo chiaro e deterministico per la progettazione e la programmazione di programmi, ci sarebbe solo un ultimo programma per gli umani da scrivere - il programma che scriverà automaticamente tutti i programmi futuri senza l'intervento umano.

    
risposta data 07.04.2011 - 06:04
fonte
0

Disegnare le linee di astrazione appropriate è qualcosa che attingi all'esperienza. Farai degli errori nel fare ciò che è innegabile.

SOLID è una forma concentrata di quell'esperienza che ti viene consegnata da persone che hanno acquisito questa esperienza nel modo più duro. L'hai praticamente inchiodato quando dici che eviterai di creare astrazioni prima di vederne il bisogno. Bene, l'esperienza SOLIDA ti aiuterà a risolvere i problemi non sei mai esistito di nuovo . Ma credimi ... ci sono davvero reali.

SOLID riguarda la modularità, la modularità riguarda la manutenibilità e la manutenibilità consiste nel consentire di mantenere un piano di lavoro umano una volta che il software raggiunge la produzione e il client inizia a notare i bug che non hai.

Il più grande vantaggio della modularità è la testabilità. Più il tuo sistema è modulare, più è facile da testare e più velocemente puoi costruire solide fondamenta per andare avanti con gli aspetti più difficili del tuo problema. Quindi il tuo problema potrebbe non richiedere questa modularità, ma solo il fatto che ti consentirà di creare un codice di prova migliore più velocemente è un gioco da ragazzi per lottare per la modularità.

Essere agili significa tentare di raggiungere il delicato equilibrio tra la spedizione veloce e la qualità. Agile non significa che dovremmo tagliare l'angolo, infatti, i progetti agili di maggiore successo in cui sono stato coinvolto sono quelli che hanno prestato molta attenzione a tali linee guida.

    
risposta data 07.04.2011 - 06:14
fonte
0

Un punto importante che sembra essere stato trascurato è che SOLID è praticato insieme a TDD. TDD tende a evidenziare ciò che è "appropriato" nel tuo particolare contesto e aiuta ad alleviare un sacco di equivoci che sembra essere nel consiglio.

    
risposta data 14.08.2013 - 03:45
fonte

Leggi altre domande sui tag