Quanti schemi di design e livelli di astrazione sono necessari? [chiuso]

29

Come posso dire che il mio software ha troppa astrazione e troppi pattern di progettazione, o viceversa, come faccio a sapere se ne dovrebbe avere di più?

Gli sviluppatori con cui lavoro stanno programmando in modo diverso su questi punti.

Alcuni astraggono ogni piccola funzione, usano schemi di progettazione ovunque sia possibile ed evitano ridondanze ad ogni costo.

Gli altri, incluso me, cercano di essere più pragmatici e scrivono codice che non si adatta perfettamente ad ogni schema di progettazione, ma è molto più veloce da capire perché viene applicata meno astrazione.

So che questo è un compromesso. Come posso sapere quando c'è abbastanza astrazione nel progetto e come faccio a sapere che ha bisogno di più?

Esempio, quando un livello di memorizzazione nella cache generico viene scritto usando Memcache. Abbiamo davvero bisogno di Memcache , MemcacheAdapter , MemcacheInterface , AbstractCache , CacheFactory , CacheConnector , ... oppure è più semplice mantenere e ancora un buon codice quando si usa solo metà di quelle classi?

Trovato su Twitter:

( link )

    
posta DanFromGermany 22.06.2017 - 13:47
fonte

11 risposte

52

Quanti ingredienti sono necessari per un pasto? Di quante parti hai bisogno per costruire un veicolo?

Sai che hai un'astrazione troppo piccola quando una piccola modifica implementazione porta a una cascata di modifiche su tutto il tuo codice. Una corretta astrazione aiuterebbe ad isolare la parte del codice che deve essere modificata.

Sai che hai troppa astrazione quando un piccolo cambiamento interfaccia porta a una cascata di cambiamenti su tutto il tuo codice, a diversi livelli. Invece di modificare l'interfaccia tra due classi, ti ritrovi a modificare dozzine di classi e interfacce solo per aggiungere una proprietà o modificare un tipo di argomento di un metodo.

A parte questo, non c'è davvero modo di rispondere alla domanda dando un numero. Il numero di astrazioni non sarà lo stesso da un progetto all'altro, da una lingua all'altra e persino da uno sviluppatore a un altro.

    
risposta data 22.06.2017 - 14:09
fonte
24

Il problema con gli schemi di progettazione può essere riassunto con il proverbio "quando si tiene un martello, tutto sembra un chiodo". L'atto di applicare un modello di design non sta migliorando il tuo programma. In realtà, direi che stai facendo un programma più complicato se aggiungi un pattern di design. La domanda rimane se non stai facendo un buon uso del modello di progettazione o meno, e questo è il cuore della domanda, "Quando abbiamo troppa astrazione?"

Se stai creando un'interfaccia e una super classe astratta per una singola implementazione, hai aggiunto due componenti aggiuntivi al tuo progetto che sono superflui e inutili. Il punto di fornire un'interfaccia è di essere in grado di gestirlo in modo uguale per tutto il tuo programma senza sapere come funziona. Il punto di una super classe astratta è di fornire un comportamento di base per le implementazioni. Se si dispone solo di un'implementazione one , si ottengono tutte le interfacce di complicazione e le classi di abstact forniscono e nessuno dei vantaggi.

Allo stesso modo, se stai usando un pattern Factory, e ti ritrovi a lanciare una classe per utilizzare la funzionalità disponibile solo nella super classe, il pattern Factory non aggiunge alcun vantaggio al tuo codice. Hai solo aggiunto una classe aggiuntiva al tuo progetto che avrebbe potuto essere evitata.

TL; DR Il mio punto è che l'obiettivo dell'astrazione non è astratto in se stesso. Serve uno scopo pratico nel tuo programma, e prima di decidere di utilizzare un modello di progettazione o creare un'interfaccia, dovresti chiederti se, così facendo, il programma è più facile da capire nonostante il la complessità o il programma è più robusto nonostante l'ulteriore complessità (preferibilmente entrambe). Se la risposta è no o forse, prendi un paio di minuti per considerare perché volevi farlo e se forse può essere fatto in un modo migliore invece senza necessariamente il bisogno di aggiungere astrazione al tuo codice.

    
risposta data 22.06.2017 - 14:41
fonte
6

TL: DR;

Non penso che ci sia un numero "necessario" di livelli di astrazioni sotto che c'è troppo poco o al di sopra del quale c'è troppo. Come nella progettazione grafica, un buon design OOP dovrebbe essere invisibile e dovrebbe essere dato per scontato. Il cattivo design sporge sempre come un pollice dolente.

Risposta lunga

Molto probabilmente non saprai mai quanti livelli di astrazione stai sviluppando.

La maggior parte dei livelli di astrazione ci sono invisibili e noi li diamo per scontati.

Questo ragionamento mi porta a questa conclusione:

Uno degli scopi principali dell'astrazione è salvare il programmatore nella necessità di tenere a mente tutto il funzionamento del sistema. Se il design ti obbliga a sapere troppo sul sistema per aggiungere qualcosa, probabilmente c'è un'astrazione troppo piccola. Penso che una brutta astrazione (design scadente, design anemico o over-engineering) possa anche costringerti a sapere troppo per aggiungere qualcosa. In un estremo abbiamo un design basato su una classe di Dio o un gruppo di DTO, nell'altro estremo abbiamo alcune strutture di OR / persistenza che ti fanno saltare attraverso anelli innumerevoli per ottenere un mondo ciao. Entrambi i casi ti costringono anche a conoscere troppo.

La brutta astrazione aderisce a una campana di Gauss nel fatto che una volta superato un punto debole inizia a mettersi in mezzo. Una buona astrazione, d'altra parte, è invisibile e non può essercene troppa perché non ti accorgi che è lì. Pensa a quanti livelli su strati di API, protocolli di rete, librerie, librerie OS, file system, livelli harware, ecc. La tua applicazione è costruita e dà per scontata.

Altro scopo principale dell'astrazione è la compartimentazione, quindi gli errori non permeano oltre una certa area, non diversamente dal doppio scafo e i serbatoi separati impediscono a una nave di allagare completamente quando una parte dello scafo ha un buco. Se le modifiche al codice finiscono per creare bug in aree apparentemente non correlate allora è probabile che ci sia un'astrazione troppo piccola.

    
risposta data 22.06.2017 - 15:34
fonte
4

I modelli di progettazione sono semplicemente soluzioni comuni ai problemi. È importante conoscere i modelli di progettazione, ma sono solo sintomi di codice ben progettato (un buon codice può ancora essere vuoto della banda di quattro set di schemi di progettazione), non la causa.

Le astrazioni sono come recinzioni. Aiutano a separare le regioni del tuo programma in blocchi testabili e intercambiabili (requisiti per realizzare codice non rigido non fragile). E molto simile alle recinzioni:

  • Desideri le astrazioni nei punti di interfaccia naturali per ridurne al minimo le dimensioni.

  • Non vuoi cambiarli.

  • Vuoi che separino le cose che possono essere indipendenti.

  • Avere uno nel posto sbagliato è peggio che non averlo.

  • Non dovrebbero avere grandi perdite .

risposta data 22.06.2017 - 18:32
fonte
4

refactoring

Non ho visto la parola "refactoring" menzionata nemmeno una volta finora. Quindi, eccoci:

Sentiti libero di implementare una nuova funzione nel modo più diretto possibile. Se hai una sola, semplice, classe, probabilmente non hai bisogno di un'interfaccia, una superclasse, una fabbrica, ecc. Per questo.

Se e quando noti che espandi la classe in modo che diventi troppo grassa, allora è il momento di strapparla. A quel tempo ha senso pensare a come in realtà dovresti farlo.

I pattern sono uno strumento mentale

I pattern, o più specificamente il libro "Design Patterns" della gang di quattro, sono grandi, tra le altre ragioni, perché costruiscono un linguaggio per gli sviluppatori per pensare e parlare. È facile dire "osservatore", " fabbrica "o" facciata "e tutti sanno esattamente cosa significa, subito.

Quindi la mia opinione sarebbe che ogni sviluppatore dovrebbe avere una conoscenza passata su almeno gli schemi del libro originale, semplicemente per essere in grado di parlare dei concetti OO senza dover sempre spiegare le basi. Dovresti usare i pattern ogni volta che appare la possibilità di farlo? Molto probabilmente no.

Biblioteche

Le biblioteche sono probabilmente l'unica area in cui può essere, al fine di sbagliare dal lato di troppe scelte basate su pattern invece di troppo poco. Cambiare qualcosa da una classe "grassa" a qualcosa con più derivazioni tratte dal modello (di solito significa più classi più piccole) cambierà radicalmente l'interfaccia; e questa è l'unica cosa che di solito non vuoi cambiare in una libreria, perché è l'unica cosa che è di reale interesse per l'utente della tua libreria. A loro non importa meno di come gestisci internamente le tue funzionalità, ma loro si preoccupano molto se cambiano costantemente il loro programma quando fai una nuova versione con una nuova API.

    
risposta data 23.06.2017 - 00:10
fonte
2

Il punto dell'astrazione dovrebbe essere innanzitutto il valore che viene portato al consumatore dell'astrazione, cioè il cliente dell'astrazione, gli altri programmatori e spesso te stesso.

Se, come cliente che sta consumando l'astrazione (s), trovi che è necessario combinare e abbinare molte diverse astrazioni per portare a termine il tuo lavoro di programmazione, allora ci sono potenzialmente troppe astrazioni.

Idealmente, la stratificazione dovrebbe riunire un numero di astrazioni più basse e sostituirlo con un'astrazione semplice e di livello superiore che i suoi consumatori possono utilizzare senza dover affrontare alcuna di quelle astrazioni sottostanti. Se hanno a che fare con le astrazioni sottostanti, il livello perde (per essere incompleto). Se il consumatore ha a che fare con troppe astrazioni diverse, forse manca il layering.

Dopo aver considerato il valore delle astrazioni per i programmatori che consumano, possiamo rivolgerci per valutare e prendere in considerazione l'implementazione, come quella della DRY-ness.

Sì, si tratta di semplificare la manutenzione, ma dovremmo considerare prima di tutto la difficile manutenzione dei consumatori, fornendo astrazioni e livelli di qualità, quindi prendere in considerazione la possibilità di semplificare la manutenzione in termini di aspetti di implementazione come evitare la ridondanza.

Example, when a generic caching layer is written using Memcache. Do we really need Memcache, MemcacheAdapter, MemcacheInterface, AbstractCache, CacheFactory, CacheConnector, ... or is this easier to maintain and still good code when using only half of those classes?

Dobbiamo guardare al punto di vista del cliente, e se le loro vite sono rese più facili allora è buono. Se le loro vite sono più complesse, allora è male. Tuttavia, potrebbe essere che ci sia un livello mancante che avvolge queste cose in qualcosa di semplice da usare. Internamente, questi potrebbero benissimo migliorare la manutenzione dell'implementazione. Tuttavia, come sospetti, è anche possibile che sia semplicemente sovrascritto.

    
risposta data 22.06.2017 - 18:10
fonte
2

L'astrazione è progettata per rendere il codice più facile da capire. Se uno strato di astrazione renderà le cose più confuse, non farlo.

L'obiettivo è utilizzare il numero corretto di astrazioni e interfacce per:

  • minimizza i tempi di sviluppo
  • massimizza la manutenibilità del codice

Riassunto solo quando richiesto

  1. Quando scopri che stai scrivendo una super classe
  2. Quando consentirà un significativo riutilizzo del codice
  3. Se l'astrazione renderà il codice significativamente più chiaro e più facile da leggere

Non astratto quando

  1. Farlo non avrà alcun vantaggio nel riutilizzo o nella chiarezza del codice
  2. Farlo renderà il codice notevolmente più lungo / più complesso senza alcun vantaggio

Alcuni esempi

  • Se hai intenzione di avere una sola cache nell'intero programma, non farlo astratto a meno che non si pensi che si rischia di finire con una superclasse
  • Se hai tre diversi tipi di buffer, usa un'astrazione di interfaccia comune a tutti loro
risposta data 23.06.2017 - 09:43
fonte
2

Penso che questa potrebbe essere una meta-risposta controversa, e sono un po 'in ritardo per la festa, ma penso che sia molto importante menzionarlo qui, perché penso di sapere da dove vieni.

Il problema con il modo in cui vengono utilizzati i modelli di progettazione è che, quando vengono insegnati, presentano un caso come questo:

You have this specific scenario. Organize your code this way. Here's a smart-looking, but somewhat contrived example.

Il problema è che quando inizi a fare l'ingegneria vera e propria, le cose non sono del tutto trucidanti. Il modello di progettazione che leggi non si adatta perfettamente al problema che stai cercando di risolvere. Per non parlare del fatto che le librerie che stai usando violano totalmente tutto ciò che è stato enunciato nel testo, spiegando questi modelli, ognuno nel suo modo speciale. E come risultato, il codice che scrivi "si sente sbagliato" e tu fai domande come questa.

In aggiunta a questo, vorrei citare Andrei Alexandrescu, quando si parla di ingegneria del software, che afferma:

Software engineering, maybe more than any other engineering discipline, exhibits a rich multiplicity: You can do the same thing in so many correct ways, and there are infinite nuances between right and wrong.

Forse è un po 'esagerato, ma penso che questo spieghi perfettamente un ulteriore motivo per cui potresti sentirti meno sicuro del tuo codice.

È in momenti come questo che la voce profetica di Mike Acton, motore di gioco protagonista di Insomniac, mi urla in testa:

KNOW YOUR DATA

Sta parlando degli input per il tuo programma e delle uscite desiderate. E poi c'è questa gemma di Fred Brooks del Mythical Man Month:

Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won’t usually need your flowcharts; they’ll be obvious.

Quindi, se fossi in te, ragionerei sul mio problema in base al mio tipico caso di input e se otterrà l'output corretto desiderato. E fai domande come questa:

  • I dati di output del mio programma sono corretti?
  • Viene prodotto in modo efficiente / veloce per il mio caso di input più comune?
  • Il mio codice è abbastanza facile da ragionare a livello locale, sia per me che per i miei compagni di squadra? In caso contrario, posso semplificarlo?

Quando lo fai, la domanda su "quanti livelli di astrazione o schemi di progettazione sono necessari" diventa molto più facile rispondere. Quanti strati di astrazione hai bisogno? Tanti quanti sono necessari per raggiungere questi obiettivi, e non di più. "E i modelli di design? Non ne ho mai usato nessuno!" Bene, se gli obiettivi di cui sopra sono stati raggiunti senza l'applicazione diretta di un modello, allora va bene. Falla funzionare e passa al prossimo problema. Inizia dai tuoi dati, non dal codice.

    
risposta data 23.06.2017 - 12:08
fonte
2

L'architettura del software sta inventando le lingue

Con ogni livello di software, crei la lingua che tu (oi tuoi colleghi di lavoro) vogliono esprimere la loro prossima soluzione di livello superiore in (quindi, inserirò alcuni analoghi in linguaggio naturale nel mio post). I tuoi utenti non vogliono passare anni a imparare a leggere o scrivere quella lingua.

Questa vista mi aiuta nel decidere i problemi di architettura.

La leggibilità

Quella lingua dovrebbe essere facilmente compresa (rendendo leggibile il codice del prossimo livello). Il codice viene letto molto più spesso di quanto scritto.

Un concetto dovrebbe essere espresso con una sola parola: una classe o un'interfaccia dovrebbero esporre il concetto. (Solitamente le lingue slave hanno due parole diverse per un verbo inglese, quindi devi imparare due volte il vocabolario.Tutte le lingue naturali usano parole singole per concetti multipli).

I concetti che esponi non dovrebbero contenere sorprese. Si tratta principalmente di convenzioni di denominazione come i metodi get e set etc. I modelli di progettazione possono essere d'aiuto perché forniscono un modello di soluzione standard e il lettore vede "OK, ottengo gli oggetti da una fabbrica" e sa cosa significa. Ma se semplicemente l'istanziazione di una classe concreta fa il lavoro, preferirei che.

Usabilità

La lingua dovrebbe essere facile da usare (facilitando la formulazione di "frasi corrette").

Se tutte queste classi / interfacce di MemCache diventano visibili al livello successivo, ciò crea una curva di apprendimento ripida per l'utente fino a quando non capisce quando e dove usare quale di queste parole per il singolo concetto di cache.

L'esposizione solo delle classi / metodi necessari facilita l'individuazione dell'utente di ciò di cui ha bisogno (vedere la citazione di DocBrowns di Antoine de Saint-Exupery). L'esposizione di un'interfaccia invece della classe di implementazione può semplificare questo aspetto.

Se esponi una funzionalità in cui è possibile applicare un modello di progettazione stabilito, è meglio seguire lo schema di progettazione piuttosto che inventare qualcosa di diverso. Il tuo utente comprenderà le API seguendo un modello di progettazione più facilmente di un concetto completamente diverso (se conosci l'italiano, lo spagnolo sarà più facile per te che il cinese).

Sommario

Introduci delle astrazioni se questo rende l'utilizzo più semplice (e vale la pena di mantenere sia l'astrazione che l'implementazione).

Se il tuo codice ha un sotto compito (non banale), risolvilo "il modo previsto", ad esempio segui il modello di progettazione appropriato invece di reinventare un diverso tipo di ruota.

    
risposta data 23.06.2017 - 12:33
fonte
1

La cosa importante da considerare è quanto deve sapere il codice consumante che effettivamente gestisce la logica aziendale riguardo a queste classi correlate alla memorizzazione nella cache. Idealmente il tuo codice dovrebbe interessare solo l'oggetto cache che vuole creare e forse un factory per creare quell'oggetto se un metodo di costruzione non è sufficiente.

Il numero di pattern usati o il livello di ereditarietà non è troppo importante a condizione che ciascun livello possa essere giustificato ad altri sviluppatori. Questo crea un limite informale poiché ogni livello aggiuntivo è più difficile da giustificare. La parte più importante è il modo in cui i livelli di astrazione sono influenzati dalle modifiche ai requisiti funzionali o aziendali. Se è possibile apportare una modifica a un solo livello per un singolo requisito, è probabile che non si sia eccessivamente sottratti o astratti, se si modifica lo stesso livello per più modifiche non correlate è probabile che si sia in astratto e si debbano separare ulteriormente le preoccupazioni.

    
risposta data 22.06.2017 - 14:55
fonte
-1

In primo luogo, la citazione di Twitter è falsa. I nuovi sviluppatori devono creare un modello, le astrazioni in genere li aiutano a "ottenere l'immagine". A condizione che le astrazioni abbiano un senso, naturalmente.

In secondo luogo, il tuo problema non è troppe o troppo poche astrazioni, è che apparentemente nessuno può decidere su queste cose. Nessuno possiede il codice, nessun singolo piano / design / filosofia è implementato, qualsiasi altro ragazzo può fare qualunque cosa diavolo sembra essere adatta per quel momento. Qualsiasi stile tu scelga, dovrebbe essere uno.

    
risposta data 22.06.2017 - 18:10
fonte