Rompendo classi e metodi in unità più piccole

3

Durante le revisioni del codice, un paio di sviluppatori hanno raccomandato di suddividere i miei metodi in metodi più piccoli.

La loro giustificazione era (1) una maggiore leggibilità e (2) la traccia posteriore che ritorna dalla produzione mostrando che il nome del metodo è più specifico della linea di codice che ha fallito. Potrebbero esserci anche delle parole colorate sulla programmazione funzionale.

Inoltre, penso che potrei aver fallito un'intervista qualche tempo fa perché non ho dato una risposta accettabile su quando interrompere le cose.

La mia inclinazione è che quando vedo un mucchio di metodi in una classe o attraverso un mucchio di file, non mi è chiaro come fluiscono insieme e quante volte ognuno viene chiamato. Non ho davvero un buon feeling per la linearità di esso tanto rapidamente solo osservandolo.

L'altra cosa è che molte persone sembrano privilegiare l'organizzazione rispetto ai contenuti (es. "Guarda come è organizzato il mio cassetto per calze!" Io: "In generale, penso che potrei arrivare più velocemente ai miei calzini se conti il tempo necessario per organizzarli ').

I nostri requisiti di business non sono molto stabili. Temo che se le classi / metodi sono molto granulari ci vorrà più tempo per rifattorizzare le modifiche dei requisiti. Non sono sicuro di quale dovrebbe essere il fattore.

In ogni caso, l'informatica è in parte arte / scienze parziali, ma non sono sicuro di quanto questo si applichi a questo problema.

EDIT:

Guarda: Qual è la lunghezza ideale di un metodo per te?

    
posta micahhoover 11.06.2014 - 19:17
fonte

5 risposte

4

È strettamente legato all'idea che sta dietro al test delle unità ... dove "unità" è un termine strongmente contestato e mal definito. Fondamentalmente, se inizi a ridimensionare il tuo codice, a che punto colpisci dei bei pacchetti che si comportano in modo non sorprendente? È bello esercitare le tue unità concettuali prima che si integrino nel tutto.

In OO, è anche guidato dal Principio di Responsabilità Unica, che afferma basicamente che ogni classe dovrebbe fare bene una cosa e una cosa. Cioè, dovresti essere in grado di pensare a ciascuna cosa nel tuo sistema facilmente senza dover ricorrere alle dita delle mani e dei piedi per contare.

Al centro, guardo il mio codice e cerco di identificare idee distinte. Di solito queste idee hanno alcune proprietà e un insieme di operazioni che hanno senso su di loro. Se l'idea è davvero solo un'idea, allora le probabilità che sia utile in più di un posto salgono.

Quando hai definito il codice attorno ai dati e alle operazioni relative alle idee principali, puoi iniziare a mettere insieme le tue idee chiare e definite in un modo che diventa un grande programma.

Diciamo che stai scrivendo un'app Sock Drawer. Alcune delle idee che dovrai modellare sono: un cassettone, un cassetto, un calzino, un paio di calzini. Ognuno di questi è abbastanza facile da definire in termini di attributi e operazioni che è possibile eseguire su di essi. Una volta che hai definito, scritto e testato queste cose, puoi iniziare a metterle insieme per creare un intero sistema.

Diciamo che la versione 2.0 del tuo Sock Drawer richiede un nastro trasportatore a razzo e un modulo AI black-box che seleziona calze per te in base alle condizioni atmosferiche, da tutto il mondo. Se hai le tue idee principali, puoi iniziare a inserirle nella logica AI e nelle funzionalità estese. Il tuo sistema può crescere aggiungendo ricchezza nell'interazione di idee chiave, senza troppe idee centrali troppo.

...

Un paio di esercizi pratici che ti aiuteranno a rompere le cose sono: 1) limitare la dimensione delle tue unità di codifica, 2) limitare la quantità di indentazione che ti permetti (non più di 3 livelli nidificati !!!!!! ma prova non più di 2 come esercizio) 3) segui "non ripeterti" religiosamente.

Tutti questi sono criteri abbastanza oggettivi che ti costringono a riflettere più duramente su come trovare schemi e consolidare il codice come in pacchetti coesivi.

    
risposta data 11.06.2014 - 22:44
fonte
4

Quando ho iniziato a scrivere il codice (e ancora un po 'ora) ho pensato più o meno allo stesso modo di te:

Why break things apart, when it's so much easier to find everything in this one place?

Fino a quando ho iniziato a ottenere applicazioni sempre più grandi e più grandi. Ora, scrivendo i miei contenuti da zero, sto vedendo sempre più opportunità di segmentare, disaccoppiare e riutilizzare.

Mentalmente devi comprendere la natura procedurale (a meno di threading) di come il computer eseguirà la tua applicazione. Sta iniziando, recuperando, memorizzando nella cache e riproducendo nel modo in cui l'hai detto, anche quando usi gli oggetti. Quindi se aiuta, come fa per me, organizzare i file visivamente nel modo in cui ti aspetti che scorrano in modo logico.

Ad esempio, potrei avere 3 file aperti alla volta: Modello, Visualizza, Modello.

Le schede saranno nel mio IDE in questo ordine. Il mio cervello legge che "ottieni dati", "fai qualcosa con i dati", "visualizza dati".

Quindi, all'interno di ciascuno di questi file, le classi e i loro metodi sono organizzati in modo simile. Anche se non è specificamente necessario avere __init__ o main() in un ordine specifico nella maggior parte dei linguaggi, ha certamente senso mettere quella cosa prima quando stai scrivendo. Ora estrapola lo stesso processo di pensiero contro l'intero progetto.

Mentre scrivi e ti trovi a dire "Hm, ho bisogno di fare xzy qui". controlla se xyz era già stato scritto. Se fosse stato in grado di estrarre quella cosa da qualunque luogo fosse, trasformarla in una funzione riutilizzabile. Ora entrambi i metodi fanno la stessa identica cosa e ti sei risparmiato il tempo. Sia ora che in futuro quando devi fare di nuovo xyz.

    
risposta data 11.06.2014 - 20:02
fonte
3

In qualsiasi buon linguaggio è facile comporre due piccole funzioni in una funzione più grande, ma non c'è modo di dividere una grande funzione in due funzioni più piccole. Allo stesso modo, è più facile capire una grande funzione in termini di funzioni più piccole rispetto a quella di tenere traccia di tutto lo stato in una grande funzione monolitica. La memoria umana è un premio; se potessimo monitorare in modo affidabile grandi quantità di dati, dovremmo semplicemente codificare in assembly.

    
risposta data 11.06.2014 - 19:46
fonte
2

Mancano tre punti importanti. In primo luogo, l'utilizzo di funzioni più piccole ben progettate e ben progettate significa che di solito non è necessario capire come tutte le funzioni fluiscono insieme e quanto spesso vengono chiamate. Osservi il livello di astrazione di cui hai bisogno e non cercare di mantenere l'intero stack di chiamate nella tua testa in una volta.

Ad esempio, se sto leggendo un algoritmo e ho trovato number.isPrime , ho una buona idea di cosa faccia. Riesco a eseguire il debug perfettamente a livello di astrazione e devo solo scavare più a fondo uno strato se scopro che restituisce risultati errati. Certo, è un po 'di lavoro in più se devo andare lì, ma di solito non lo faccio.

D'altra parte, se l'autore originale non ha scomposto isPrime nella propria funzione, non ho scelta ma di eseguire lo slog attraverso quel livello aggiuntivo di astrazione ogni volta.

In secondo luogo, le funzioni più piccole sono più facili da testare e eseguire il debug, perché fanno solo una cosa e quindi hanno maggiori probabilità di essere corrette. isPrime è abbastanza facile da testare con alcuni dati di esempio in condizioni al limite, quindi puoi fidarti di questo dopo quel punto. Se è sepolto in linea all'interno di un'altra funzione, è difficile isolare eventuali problemi.

Infine, è più probabile che le funzioni più piccole siano riutilizzabili. Quando usi funzioni più piccole, troverai naturalmente dei posti per evitare la duplicazione e sarà facile consolidare. Inoltre, le funzioni riutilizzate sono già testate e debuggate, risparmiando tempo.

Non è tanto la differenza tra un cassetto di calze organizzato o disorganizzato, come tra un calzino e una macchina per maglieria. Anche se una macchina per maglieria può sputare 10 calze al minuto, non è ancora il livello di astrazione che desideri quando ti vesti di mattina.

    
risposta data 11.06.2014 - 23:52
fonte
1

Oltre alle altre buone risposte sopra, ti suggerisco di controllare diversi studi di sviluppo software che dimostrano che altamente coesivo sono più facili da capire e ragionare. Più idee sono bloccate in una funzione / classe / modulo più bassa è la coesione. Una minore coesione (tra le altre cose) porta a tassi di comprensione più bassi.

    
risposta data 11.06.2014 - 22:25
fonte

Leggi altre domande sui tag