Astrazione: La guerra tra risolvere il problema e una soluzione generale [chiuso]

33

Come programmatore, mi trovo nel dilemma in cui voglio rendere il mio programma il più astratto e il più generale possibile.

Farlo di solito mi permetterebbe di riutilizzare il mio codice e avere una soluzione più generale per un problema che potrebbe (o potrebbe non esserlo) tornare di nuovo.

Quindi questa voce nella mia testa dice, basta risolvere il problema fittizio è così facile! Perché dedicare più tempo del necessario?

Abbiamo tutti affrontato questa domanda in cui l'astrazione è sulla tua spalla destra e Solve-it-stupid si siede a sinistra.

Quale ascoltare e quanto spesso? Qual è la tua strategia per questo? Dovresti astrarre tutto?

    
posta Bryan Harrington 31.12.2010 - 23:15
fonte

9 risposte

27

Which to listen to and how often?

Non astrarre mai fino a quando non è necessario.

In Java, ad esempio, è necessario utilizzare le interfacce. Sono un'astrazione.

In Python non hai interfacce, hai Duck Typing e non hai bisogno di alti livelli di astrazione. Quindi non lo fai.

What is your strategy for this?

Non astrarre fino a quando non lo hai scritto tre volte.

Una volta è - bene - una volta. Basta risolverlo e andare avanti.

Due volte indica che potrebbe esserci uno schema qui. O forse no. Può essere solo una coincidenza.

Tre volte è l'inizio di un modello. Ora trascende la coincidenza. Ora puoi astrarre con successo.

Should you abstract everything?

No.

In effetti, non dovresti mai astrarre nulla finché non hai la prova assoluta che stai facendo il giusto tipo di astrazione. Senza la "Regola delle tre ripetizioni", scriverai cose che sono inutilmente astratte in un modo che non è di aiuto.

Doing so usually would allow me to reuse my code

Questa è un'ipotesi che è spesso falsa. L'astrazione potrebbe non aiutare affatto. Può essere fatto male. Pertanto, non farlo finché non devi.

    
risposta data 31.12.2010 - 23:52
fonte
19

Ah, YAGNI. Il concetto di programmazione più abusato.

C'è una differenza tra rendere generico il tuo codice e fare un lavoro extra. Dovresti dedicare più tempo a rendere il tuo codice liberamente accoppiato e facilmente adattato per fare altre cose? Assolutamente. Dovresti passare il tempo a implementare funzionalità non necessarie? No. Dovresti passare il tempo a far funzionare il tuo codice con un altro codice che non è ancora stato implementato? No.

Come dice il proverbio "fai la cosa più semplice che possa funzionare". Il fatto è che le persone confondono sempre semplici con facili . La semplicità richiede lavoro. Ma vale la pena.

Puoi andare fuori bordo? Ovviamente. Ma la mia esperienza è che poche aziende hanno bisogno di più di un atteggiamento "finisci ora" di quello che hanno già. La maggior parte delle aziende ha bisogno di più persone che pensino le cose in anticipo. Altrimenti, finiscono sempre per avere un problema autosufficiente in cui nessuno ha mai tempo per niente, tutti sono sempre di fretta e nessuno fa qualcosa.

Pensaci in questo modo: è probabile che il tuo codice possa essere riutilizzato in qualche modo. Ma non prevedi correttamente in questo modo. Quindi rendi il tuo codice pulito, astratto e liberamente abbinato. Ma non cercare di far funzionare il tuo codice con funzionalità specifiche che non esistono ancora. Anche se sai che esisterà in futuro.

    
risposta data 31.12.2010 - 23:59
fonte
12

Un sillogismo:

  1. La generalità è costosa.

  2. Stai spendendo i soldi degli altri.

  3. Pertanto la spesa di generalità deve essere giustificata agli stakeholder.

Chiediti se sei veramente in grado di risolvere il problema più generale al fine di salvare i soldi delle parti interessate in un secondo momento o se trovi solo una sfida intellettuale per creare una soluzione inutilmente generale.

Se la generalità è determinata per essere desiderabile allora dovrebbe essere progettato e testato come qualsiasi altra caratteristica . Se non riesci a scrivere test che dimostrano come la generalità implementata risolva un problema richiesto dalle specifiche, non preoccuparti di farlo! Una funzionalità che non soddisfa i criteri di progettazione e non può essere testata è una funzionalità su cui nessuno può fare affidamento.

E infine, non esiste "il più generale possibile". Supponiamo che tu scriva software in C #, solo per ragioni di discussione. Hai intenzione di fare in modo che ogni classe implementi un'interfaccia? Ogni classe è una classe base astratta con ogni metodo un metodo astratto? È piuttosto generale, ma non è affatto "il più generale possibile". Ciò consente alle persone di cambiare l'implementazione di qualsiasi metodo attraverso la sottoclasse. Cosa succede se vogliono cambiare l'implementazione di un metodo senza sottoclassi? Potresti rendere ogni metodo effettivamente una proprietà di tipo delegato, con un setter in modo che le persone possano cambiare ogni metodo con qualcos'altro. Ma cosa succede se qualcuno vuole aggiungere altri metodi? Ora ogni oggetto deve essere espandibile.

A questo punto dovresti abbandonare C # e abbracciare JavaScript. Ma non sei ancora arrivato abbastanza vicino al generale; cosa succede se qualcuno vuole cambiare il modo in cui la ricerca dei membri funziona per questo particolare oggetto? Forse dovresti scrivere tutto in Python invece.

Aumentare la generalità significa spesso abbandonare la prevedibilità e aumentare in modo massivo i costi dei test per garantire che la generalità implementata riesca effettivamente a soddisfare le reali esigenze degli utenti . Tali costi sono giustificati dai loro benefici per gli stakeholder che stanno pagando per questo? Forse lo sono; spetta a te discutere con gli stakeholder. I miei stakeholder non sono affatto disposti a rinunciare alla tipizzazione statica alla ricerca di un livello di generalità del tutto inutile ed estremamente costoso, ma forse sono i tuoi.

    
risposta data 03.01.2011 - 17:05
fonte
5

Per essere chiari, fare qualcosa di generalizzato e implementare un'astrazione sono due cose completamente diverse.

Ad esempio, considera una funzione che copia la memoria.

La funzione è un'astrazione che nasconde il modo in cui i 4 byte vengono copiati.

int copy4Bytes (char * pSrc, char * pDest)

Una generalizzazione renderebbe questa funzione copia qualsiasi numero di byte.

int copyBytes (char * pSrc, char * pDest, int numBytesToCopy)

L'astrazione si presta a riutilizzare, mentre la generalizzazione rende l'astrazione utile in molti casi.

Più specificamente correlato alla tua domanda, l'astrazione non è utile solo dal punto di vista del riutilizzo del codice. Spesso, se fatto correttamente, renderà il tuo codice più leggibile e manutenibile. Usando l'esempio sopra, cosa è più facile da leggere e capire se si sta sfogliando il codice copyBytes () o un ciclo for che itera attraverso un array spostando i dati di un indice alla volta? L'astrazione può fornire una sorta di auto-documentazione che a mio parere rende il codice più facile da utilizzare.

Come regola generale, se riesco a trovare un buon nome di funzione che descriva esattamente cosa intendo fare un pezzo di codice, scrivo una funzione a prescindere dal fatto che io pensi di usarlo o meno ancora una volta.

    
risposta data 01.01.2011 - 00:19
fonte
4

Una buona regola generale per questo genere di cose è Zero, Uno, Infinito. Cioè, se hai bisogno di ciò che stai scrivendo più di una volta, puoi assumere che ne avrai bisogno ancora più volte e generalizzare. Questa regola implica che non ti preoccupi dell'astrazione la prima volta che scrivi qualcosa.

Un altro buon motivo per questa regola è che la prima volta che scrivi il codice, non saprai necessariamente cosa astrarre perché hai solo un esempio. La legge di Murphy implica che se scrivi codice astratto per la prima volta, il secondo esempio avrà differenze che non avevi previsto.

    
risposta data 31.12.2010 - 23:20
fonte
2

Eric Lippert indica tre cose che penso si applicano nel suo articolo verifica futura di un progetto . Penso che se lo seguirai sarai in buona forma.

First: Premature generality is expensive.

Second: Represent in your model only those things which are always in the problem domain and whose class relationships are unchanging.

Third: Keep your policies away from your mechanisms.

    
risposta data 01.01.2011 - 00:14
fonte
1

Dipende dal motivo per cui stai codificando, lo scopo del tuo progetto. Se il valore del tuo codice è quello che risolve un problema concreto, allora vuoi farlo e passare al prossimo problema. Se ci sono cose facili e veloci che puoi fare per rendere le cose più facili ai futuri utenti del codice (incluso te stesso), quindi, con tutti i mezzi, fare accomodamenti ragionevoli.

D'altra parte, ci sono casi in cui il codice che stai scrivendo ha uno scopo più generico. Ad esempio, quando stai scrivendo una libreria per altri programmatori, chi la utilizzerà in un'ampia varietà di applicazioni. I potenziali utenti sono sconosciuti e non è possibile chiedere loro esattamente ciò che vogliono. Ma tu vuoi rendere la tua biblioteca ampiamente utile. In questi casi, impiegherei più tempo a cercare di supportare la soluzione generale.

    
risposta data 01.01.2011 - 00:41
fonte
0

Sono un grande fan del K.I.S.S. linea di principio.

Mi concentro sul fornire ciò che mi viene chiesto di fare e non su quale sia la soluzione migliore. Ho dovuto lasciare andare il perfezionismo (e O.C.D.), perché mi ha reso infelice.

    
risposta data 01.01.2011 - 01:53
fonte
-1

where Abstraction is on your right shoulder and Solve-it-stupid sits on the left.

Non sono d'accordo con "risolvolo in modo stupido" , penso che possa essere più "risolverlo in modo intelligente" .

Che cos'è più intelligente:

  • Scrittura di una soluzione generalizzata complessa che può supportare più casi
  • Scrittura di un codice breve ed efficiente che risolve il problema ed è facile da mantenere e che può essere esteso in futuro SE è richiesto.

La scelta predefinita dovrebbe essere la seconda. A meno che tu non possa dimostrare un bisogno generazionale.

La soluzione generalizzata dovrebbe essere solo quando sai che è qualcosa che verrà utilizzato in più progetti / casi diversi.

Di solito trovo che le soluzioni generalizzate siano meglio utilizzate come "Codice libreria Core"

    
risposta data 03.01.2011 - 23:00
fonte

Leggi altre domande sui tag