Refactoring e principio Aperto / Chiuso

10

Recentemente sto leggendo un sito web sullo sviluppo del codice pulito (non inserisco qui un link perché non è in inglese).

Uno dei principi pubblicizzati da questo sito è il Open Closed Principle : ogni componente software dovrebbe essere aperto per l'estensione e chiuso per la modifica. Ad esempio, quando abbiamo implementato e testato una classe, dovremmo solo modificarla per correggere i bug o aggiungere nuove funzionalità (ad es. Nuovi metodi che non influenzano quelli esistenti). La funzionalità e l'implementazione esistenti non dovrebbero essere cambiate.

Normalmente applico questo principio definendo un'interfaccia I e una corrispondente classe di implementazione A . Quando la classe A è diventata stabile (implementata e testata), di solito non la modifica troppo (forse, per niente), cioè

  1. Se arrivano nuovi requisiti (es. prestazioni, o un'implementazione completamente nuova dell'interfaccia) che richiedono grandi cambiamenti al codice, scrivo una nuova implementazione B , e continuo a usare A finché B è non maturo. Quando B è maturo, tutto ciò che è necessario è cambiare il modo in cui I è istanziata.
  2. Se i nuovi requisiti suggeriscono anche una modifica all'interfaccia, definisco una nuova interfaccia I' e una nuova implementazione A' . Quindi I , A sono congelati e rimangono l'implementazione per il sistema di produzione a condizione che I' e A' non siano abbastanza stabili da sostituirli.

Quindi, in considerazione di queste osservazioni, mi ha un po 'sorpreso che la pagina web suggerisse l'uso di refactoring complessi , "... perché non è possibile scrivere codice direttamente nel suo modulo finale. "

Non c'è una contraddizione / conflitto tra l'applicazione del Principio Aperto / Chiuso e suggerire l'uso di refactoring complessi come best practice? Oppure l'idea è che si possono usare refactoring complessi durante lo sviluppo di una classe A , ma quando tale classe è stata testata con successo dovrebbe essere congelata?

    
posta Giorgio 19.10.2012 - 13:06
fonte

7 risposte

8

Penso al principio di Open-Closed come un obiettivo di design . Se finisci per doverlo violare, significa che il tuo progetto iniziale è fallito, il che è certamente possibile e anche probabile.

Refactoring significa che stai cambiando il design senza cambiare la funzionalità. Probabilmente stai cambiando il tuo design perché c'è un problema con esso. Forse il problema è che è difficile seguire il principio open-closed quando si apportano modifiche al codice esistente e si sta tentando di risolverlo.

Potresti eseguire un refactoring per consentire di implementare la tua prossima funzione senza che viola l'OCP quando lo fai.

    
risposta data 19.10.2012 - 13:49
fonte
7

Il principio di Open-Closed è più di un indicatore di quanto è stato progettato il tuo software ; non un principio da seguire alla lettera. È anche un principio che ci aiuta a impedirci di cambiare accidentalmente le interfacce esistenti (classi e metodi che chiamate e come vi aspettate che funzionino).

L'obiettivo è scrivere software di qualità. Una di queste qualità è l'estendibilità. Ciò significa che è facile aggiungere, rimuovere, modificare il codice con quelle modifiche che tendono ad essere limitate al minor numero possibile di classi esistenti. L'aggiunta di un nuovo codice è meno rischiosa rispetto alla modifica del codice esistente , quindi a questo proposito Open-Closed è una buona cosa da fare. Ma di quale codice stiamo parlando esattamente? Il crimine di violare O-C è molto meno quando puoi aggiungere nuovi metodi a una classe invece di dover modificare quelli esistenti.

O-C è frattale . Mela a tutti i livelli del tuo design. Tutti pensano che venga applicato solo a livello di classe. Ma è ugualmente applicabile a livello di metodo o a livello di assemblaggio.

La violazione troppo frequente di O-C al livello appropriato suggerisce che forse è il momento di refactoring . "Livello appropriato" è un giudizio che ha tutto a che fare con il tuo progetto generale.

Seguendo Open-Closed letteralmente significa che il numero di classi esploderà. Creerai (maiuscole "I") le interfacce inutilmente. Finirai con frammenti di funzionalità diffusi tra le classi e dovrai quindi scrivere molto più codice per collegarli tutti insieme. Ad un certo punto ti verrà in mente che cambiare la classe originale sarebbe stato meglio.

    
risposta data 19.10.2012 - 16:26
fonte
6

Il principio Open-Closed sembra essere un principio apparso prima che il TDD fosse più diffuso. L'idea è che è rischioso reimpostare il codice perché potresti rompere qualcosa, quindi è più sicuro lasciare il codice esistente così com'è e aggiungerlo semplicemente. In assenza di test questo ha senso. Lo svantaggio di questo approccio è l'atrofia del codice. Ogni volta che estendi una classe invece di refactoring, finisci con un livello extra. Stai semplicemente imbucando il codice in cima. Ogni volta che inserisci più codice, aumenti le possibilità di duplicazione. Immaginare; c'è un servizio nella mia base di codice che voglio usare, trovo che non ha quello che voglio, quindi creo una nuova classe per estenderla e includere la mia nuova funzionalità. Un altro sviluppatore arriva più tardi e vuole anche utilizzare lo stesso servizio. Sfortunatamente, non si rendono conto che la mia versione estesa esiste. Codificano rispetto all'implementazione originale ma hanno anche bisogno di una delle funzionalità che ho codificato. Invece di usare la mia versione ora estendono anche l'implementazione e aggiungono la nuova funzionalità. Ora abbiamo 3 classi, l'originale e due nuove versioni con funzionalità duplicate. Segui il principio di apertura / chiusura e questa duplicazione continuerà a crescere nel tempo di vita del progetto, portando a una base di codice inutilmente complessa.

Con un sistema ben collaudato non c'è bisogno di subire questa atrofia del codice, puoi tranquillamente effettuare il refactoring del codice permettendo al tuo design di assimilare nuovi requisiti piuttosto che dover continuamente ricorrere al nuovo codice. Questo stile di sviluppo è chiamato design emergente e porta a basi di codice che sono in grado di rimanere in buona forma per tutta la loro vita piuttosto che raccogliere gradualmente cruft.

    
risposta data 19.10.2012 - 16:54
fonte
5

In parole semplici:

Il principio

A. O / C significa che specializzazione deve essere eseguita estendendo, non modificando una classe per soddisfare esigenze specifiche.

B. L'aggiunta di funzionalità mancante (non specializzata) significa che il progetto non era completo e devi aggiungerlo alla classe base, ovviamente senza violare il contratto. Penso che questo non stia violando il principio.

C. Il refactoring non viola il principio.

Quando un design matura , ad esempio, dopo un po 'di tempo in produzione:

  • Ci dovrebbero essere pochissime ragioni per farlo (punto B), tendendo a zero nel tempo.
  • (Punto C) sarà sempre possibile anche se più raro.
  • Si suppone che tutte le nuove funzionalità siano specializzate, il che significa che le classi devono essere estese (ereditate da) (punto A).
risposta data 19.10.2012 - 15:31
fonte
1

Per me, il principio Open-Closed è una linea guida, non una regola ferrea.

Per quanto riguarda la parte aperta del principio, le classi finali in Java e le classi in C ++ con tutti i costruttori dichiarati come privati violano la parte aperta del principio open-closed. Ci sono buoni casi d'uso solidi (nota: solida, non SOLIDA) per le classi finali. Progettare per l'estensibilità è importante. Tuttavia, questo richiede una buona dose di lungimiranza e impegno, e tu stai sempre oltrepassando la linea di violazione di YAGNI (non ne avrai bisogno) e iniettando nel codice l'odore della generalità speculativa. I componenti software chiave devono essere aperti per l'estensione? Sì. Tutti? No. Di per sé è generalità speculativa.

Per quanto riguarda la parte chiusa, quando si passa dalla versione 2.0 alla 2.1 alla 2.2 alla 2.3 di qualche prodotto, il comportamento non modificante è una buona idea. Agli utenti non piace molto quando ogni versione minore interrompe il proprio codice. Tuttavia, lungo la strada si scopre spesso che l'implementazione iniziale nella versione 2.0 è stata fondamentalmente interrotta o che i vincoli esterni che limitavano il progetto iniziale non si applicano più. Lo sorridi e lo mantieni e mantieni quel design nella versione 3.0 o rendi la 3.0 non retrocompatibile per certi aspetti? La retrocompatibilità può essere un enorme ostacolo. I principali limiti di rilascio sono il luogo in cui è accettabile la rottura della compatibilità a ritroso. È necessario fare attenzione che fare ciò potrebbe far arrabbiare gli utenti. Ci deve essere una buona ragione per cui è necessaria questa rottura con il passato.

    
risposta data 19.10.2012 - 14:47
fonte
0

Il refactoring, per definizione, sta cambiando la struttura del codice senza modificare il comportamento. Quindi, quando si refactoring, non aggiungere nuove funzionalità.

Quello che hai fatto come esempio per il principio Apri Chiudi suona OK. Questo principio riguarda l'estensione del codice esistente con nuove funzionalità.

Tuttavia, non ottenere questa risposta in modo errato. Non sottintende che tu debba solo fare funzioni o solo fare refactoring per grandi blocchi di dati. Il modo più comune di programmazione è quello di fare un po 'di funzionalità rispetto a fare immediatamente un po' di refactoring (combinato con test ovviamente per essere sicuro di non aver cambiato alcun comportamento). Il refactoring complesso non significa refactoring "grande", significa applicare tecniche di refactoring complesse e ben ponderate.

Informazioni sui principi SOLID. Sono davvero delle buone linee guida per lo sviluppo del software, ma non ci sono regole religiose da seguire ciecamente. A volte, molte volte, dopo aver aggiunto una seconda e una terza e una di queste, ti rendi conto che il tuo progetto iniziale, anche se rispetta Open-Close, non rispetta altri principi o requisiti software. Ci sono punti nell'evoluzione di un design e di un software quando devono essere fatti cambiamenti più complessi. L'obiettivo è trovare e realizzare questi problemi il prima possibile e applicare le tecniche di refactoring nel miglior modo possibile.

Non esiste un design perfetto. Non esiste un tale disegno che possa e debba rispettare tutti i principi o modelli esistenti. Questa è l'utopia del codice.

Spero che questa risposta ti abbia aiutato nel tuo dilemma. Sentiti libero di chiedere chiarimenti se necessario.

    
risposta data 19.10.2012 - 13:30
fonte
-1

Secondo la mia comprensione - se si aggiungono nuovi metodi alla classe esistente, allora non interromperà l'OCP. tuttavia mi sono confuso con l'aggiunta di nuove variabili nella classe. Ma se si modifica il metodo e i parametri esistenti nel metodo esistente, si interromperà sicuramente l'OCP, poiché il codice è già testato e superato se si modifica il metodo [Quando il requisito cambia], sarà un problema.

    
risposta data 12.07.2017 - 12:04
fonte

Leggi altre domande sui tag