Quali cambiamenti sono troppo grandi per essere resi facili da una corretta progettazione?

11

Questa è una domanda piuttosto vaga, ma è qualcosa a cui non ho mai pensato di aver trovato una risposta soddisfacente quando ho letto sulla corretta progettazione.

Generalmente, quando si apprende la programmazione orientata agli oggetti, l'astrazione, il factoring, ecc., il santo graal del design - e il motivo per cui affermano sempre che stai usando le tecniche di sviluppo in questione - è che renderà il tuo programma "facile per cambiare "," manutenibile "," flessibile "o uno qualsiasi dei sinonimi utilizzati per esprimere un concetto dal suono produttivo. Marcando ivars privati, suddividendo il codice in molti piccoli metodi autonomi, mantenendo le interfacce generali, si presume che si possa ottenere la possibilità di modificare il programma con facilità e grazia.

Per modifiche relativamente piccole, questo ha funzionato bene per me. Le modifiche alle strutture di dati interne utilizzate da una classe per aumentare le prestazioni non sono mai state una grande difficoltà e non hanno nemmeno modifiche all'interfaccia utente, indipendente dall'API, come la riprogettazione di un sistema di inserimento di testo o la revisione della grafica per un elemento di gameplay .

Tutti questi cambiamenti sembrano intrinsecamente autosufficienti. Nessuno di essi comporta modifiche al comportamento o alla progettazione del componente del programma che viene modificato, per quanto riguarda il codice esterno interessato. Che tu stia scrivendo proceduralmente o in uno stile OO, con funzioni grandi o piccole, queste sono semplici modifiche da apportare anche se hai solo un design discreto.

Tuttavia, ogni volta che le modifiche diventano grandi e pelose, ovvero le modifiche all'API, nessuno dei miei preziosi "modelli" viene mai in soccorso. Il grande cambiamento rimane grande, il codice interessato rimane influenzato e molte ore di lavoro di generazione di bug si trovano davanti a me.

Quindi, la mia domanda è questa. Quanto è grande il cambiamento che la progettazione corretta pretende di essere in grado di facilitare? C'è qualche ulteriore tecnica di progettazione, a me sconosciuta o che non sono riuscita a implementare, che rende davvero semplice la modifica appiccicosa, o è quella promessa (che ho sentito pronunciare da tanti paradigmi diversi) semplicemente una buona idea, completamente disconnesso dalle verità immutabili dello sviluppo del software? C'è uno "strumento di cambiamento" che posso aggiungere al mio toolbelt?

In particolare, il problema che sto affrontando mi ha portato ai forum è questo: ho lavorato sull'implementazione di un linguaggio di programmazione interpretato (implementato in D, ma non è rilevante), e ho deciso che le mie chiusure ' gli argomenti dovrebbero essere basati su parole chiave, piuttosto che posizionali come sono attualmente. Ciò richiede la modifica di tutto il codice esistente che chiama funzioni anonime, che, fortunatamente, è piuttosto piccolo perché sono all'inizio nello sviluppo della mia lingua (< 2000 linee), ma sarebbe enorme se avessi preso questa decisione in una fase successiva . In una situazione del genere, c'è un modo in cui, con la giusta lungimiranza nel design, avrei potuto rendere questa modifica più facile, o sono certi (la maggior parte) cambiamenti intrinsecamente di vasta portata? Sono curioso di sapere se questo è in qualche modo un fallimento delle mie capacità progettuali - se lo fosse, sarei molto desideroso di migliorarle con il materiale di lettura necessario se c'è un modo per migliorare la gravità del problema.

Per essere chiari, non sono affatto scettico su OOP o su altri pattern comunemente in uso. Per me, tuttavia, le loro virtù sono nella scrittura originale, piuttosto che nel mantenimento, delle basi di codice. L'ereditarietà consente di astrarre bene schemi ripetitivi, il polimorfismo consente di separare il codice in base alla funzione comprensibile (quale classe) piuttosto che all'effetto macchina (il ramo dell'istruzione switch ) e le funzioni piccole e autonome ti permettono di scrivere in un piacevole stile "dal basso verso l'alto". Tuttavia, sono scettico nei confronti delle loro affermazioni di flessibilità.

    
posta SelectricSimian 18.03.2013 - 06:34
fonte

6 risposte

13

A volte una modifica è abbastanza grande da dover progettare un percorso di migrazione. Anche se i punti di inizio e fine sono ben progettati, spesso non puoi semplicemente cambiare il tacchino freddo. Molti progettisti altrimenti buoni non riescono a progettare buoni percorsi di migrazione.

Questo è il motivo per cui penso che ogni programmatore dovrebbe fare un software di scrittura stint per gli ambienti di produzione 24/7. C'è qualcosa in cui le perdite vengono quotate nell'ordine del tuo stipendio annuale al minuto che ti spinge a imparare a scrivere un robusto codice di migrazione.

Ciò che le persone fanno naturalmente per un grande cambiamento è strappare alla vecchia maniera, mettere nel nuovo modo, quindi passare un paio di giorni a correggere un errore di compilazione di miliardi, fino a quando il codice non sarà nuovamente compilato in modo da poter iniziare i test. Dato che il codice non è stato testabile per due giorni, improvvisamente hai un enorme casino di bug entangled da risolvere.

Per un grande cambiamento di progettazione, come il passaggio da argomenti posizionali a parole chiave, un buon percorso di migrazione sarebbe innanzitutto quello di aggiungere il supporto per gli argomenti delle parole chiave senza rimuovere il supporto posizionale . Quindi si modifica metodicamente il codice chiamante per utilizzare gli argomenti delle parole chiave. Questo è simile a come lo hai fatto prima, eccetto che questa volta puoi testare mentre vai avanti, perché il codice di chiamata invariato funziona ancora. Poiché le modifiche sono più piccole tra i test, i bug sono più facili da risolvere in precedenza. Quindi, quando tutto il codice di chiamata è stato modificato, è banale rimuovere il supporto posizionale.

Questo è il motivo per cui le API pubblicate pubblicamente deprecano i vecchi metodi invece di rimuoverli. Fornisce un percorso di migrazione senza interruzioni per chiamare il codice. Potrebbe sembrare un lavoro in più da supportare entrambi per un po ', ma ti limiti il tempo a testare.

Quindi per rispondere alla tua domanda come originariamente formulata, non ci sono quasi modifiche troppo grandi per essere rese più facili da una corretta progettazione. Se il tuo cambiamento sembra troppo grande, devi solo progettare alcune fasi intermedie temporanee per la migrazione del tuo codice.

    
risposta data 18.03.2013 - 17:18
fonte
9

Ti consiglierei di leggere il saggio Big Ball of Mud .

Fondamentalmente il punto in cui il design tende a deteriorarsi man mano che progredisci con lo sviluppo e devi dedicare il lavoro a contenere la complessità. La complessità in sé non può essere eliminata, solo contenuta, perché è inerente al problema che stai cercando di risolvere.

Questo porta ai principi principali dello sviluppo agile. I due più rilevanti sono "non ne avrai bisogno" dicendoti di non preparare il design per le funzionalità che non stai ancora implementando, perché è improbabile che comunque il disegno sia corretto e "refactoring senza pietà" che ti dice di lavorare sul mantenimento della sanità mentale del codice in tutto il progetto.

Sembra che la risposta sia che nessun design è in grado di facilitare qualsiasi cambiamento al di là di esempi inventati progettati specificatamente per dimostrare che il design è più strong di quello che realmente è.

Da un lato, dovresti essere scettico sulla programmazione orientata agli oggetti. È un ottimo strumento in molti contesti, ma devi essere consapevole dei suoi limiti. Soprattutto l'uso improprio dell'ereditarietà può portare a un codice incredibilmente contorto. Ovviamente dovresti essere altrettanto scettico riguardo a qualsiasi altra tecnica di progettazione. Ognuno ha i suoi usi e ognuno ha i suoi punti deboli. Non c'è un proiettile d'argento.

    
risposta data 18.03.2013 - 09:16
fonte
4

In termini generali, ci sono "pezzi di codice" (funzioni, metodi, oggetti, qualsiasi cosa) e "interfacce tra pezzi di codice" (API, dichiarazioni di funzioni, qualsiasi cosa, incluso il comportamento).

Se una parte di codice può essere cambiata senza cambiare l'interfaccia da cui dipendono altri elementi di codice, la modifica sarà più semplice (e non importa se usi OOP o meno).

Se una parte di codice non può essere cambiata senza cambiare l'interfaccia da cui dipendono altri pezzi di codice, allora la modifica sarà più difficile (e non importa se usi OOP o meno).

I principali vantaggi di OOP sono che le "interfacce" pubbliche sono chiaramente contrassegnate (ad esempio i metodi pubblici di un oggetto) e che l'altro codice non può utilizzare interfacce interne (ad esempio i metodi privati dell'oggetto). Poiché le interfacce pubbliche sono chiaramente contrassegnate, le persone tendono ad essere più attente a progettare quelle interfacce pubbliche (il che aiuta ad evitare i cambiamenti più difficili). Poiché le interfacce interne non possono essere utilizzate da altri codici, è possibile cambiarle senza preoccuparsi di altri codici che dipendono da loro.

    
risposta data 18.03.2013 - 09:35
fonte
2

Non esiste una metodologia di progettazione che possa salvarti dai fastidi di una grande richiesta / modifica dell'ambito. È semplicemente impossibile immaginare e tenere conto di tutte le possibili modifiche che potrebbero essere pensate in un secondo momento.

Dove un buon design fa ti aiuta ad aiutarti a capire in che modo tutte le parti si incastrano e quali saranno le conseguenze della modifica proposta.
Inoltre, un buon design può limitare le parti interessate dalla maggior parte delle modifiche proposte.

In breve, le modifiche tutte sono rese più facili da un buon design, ma un buon design non può trasformare un grande cambiamento in uno piccolo. (un cattivo / nessun disegno d'altra parte può trasformare qualsiasi piccola modifica in una grande).

    
risposta data 18.03.2013 - 10:06
fonte
1

Dal momento che il design intende portare un progetto di sviluppo da zero zero fogli a un prodotto finale affidabile e funzionante (almeno il design per uno), e questo è il più grande cambiamento possibile in un progetto, dubito che ci sia una cosa come un cambiamento troppo grande da gestire.

Se qualcosa è il contrario. Alcuni cambiamenti sono troppo piccoli per preoccuparsi di qualsiasi "progettazione" o pensiero di fantasia. Correzioni di bug semplici, per esempio.

Ricorda che gli schemi di progettazione aiutano a pensare in modo chiaro e trovano buone soluzioni progettuali per situazioni comuni, come la creazione di un numero enorme di piccoli oggetti di tipo identico. Nessuna lista di modelli è completa. Tu, e tutti noi, avremo problemi di progettazione che non sono comuni, che non si adattano a nessun buco di piccone. I tentativi di fare ogni piccola mossa in un processo di sviluppo del software secondo uno schema ufficiale o un altro, è un modo troppo religioso di fare le cose e porta a pasticci non mantenibili.

Tutti funzionano nel software è naturalmente la generazione di bug. I giocatori di calcio subiscono infortuni. I musicisti prendono calli o spasmi delle labbra a seconda del loro strumento. (Se ottieni degli spasmi al labbro mentre suoni la chitarra, stai sbagliando). Gli sviluppatori di software hanno degli errori. Amiamo trovare modi per ridurre il tasso di riproduzione, ma non sarà mai pari a zero.

    
risposta data 18.03.2013 - 08:53
fonte
1

Il costo delle modifiche radicali è spesso proporzionale alla dimensione della base di codice. Pertanto, la riduzione della dimensione della base del codice dovrebbe sempre essere uno degli obiettivi di qualsiasi progetto appropriato.

Nel tuo esempio, la corretta progettazione ha già reso più semplice il tuo lavoro: dover apportare modifiche a 2000 righe di codice base anziché a 20000 o 200000 righe di codice base.

Il design corretto riduce i cambiamenti di sweep, ma non li elimina.

I cambiamenti radicali sono facilmente automatizzabili se c'è un supporto adeguato dagli strumenti di analisi del linguaggio. Una modifica di refactoring generale può essere applicata all'intero codebase in uno stile di ricerca e sostituzione (rispettando nel modo corretto le regole linguistiche). Il programmatore deve solo esprimere un giudizio si / no per ogni hit di ricerca.

    
risposta data 18.03.2013 - 09:39
fonte

Leggi altre domande sui tag