Accoppiamento di codice introdotto da DRY e OOD

14

Sto cercando indicazioni su accoppiamento DRY vs codice. Non mi piace duplicare il mio codice e inoltre non mi piace l'accoppiamento di codice tra moduli non correlati. Quindi io refactoring il codice duplicato se trovo il codice identicamente duplicato un anno dopo che la duplicazione è stata introdotta. Tuttavia, ho sempre più sperimentato situazioni in cui il mondo reale è molto più imprevedibile e, dopo aver rifatto il codice, sorgono situazioni che richiedono di nuovo il codice.

Ad esempio, se avessi il codice per gestire auto a benzina, SUV a benzina, auto elettriche e SUV elettrici, diciamo che ho rifatto il codice duplicato nella gerarchia della "benzina" e nella gerarchia "elettrica", entrambi discendenti dal "veicolo" gerarchia. Fin qui tutto bene. E poi, la mia azienda introduce un'auto ibrida e una semi ibrida - che richiederebbe modifiche fondamentali alla mia stessa gerarchia originale. Forse richiederebbe "composizione" tra la benzina e le gerarchie elettriche.

La duplicazione del codice chiaramente non è buona perché aumenta il tempo necessario per implementare una modifica comune a tutti i prodotti sopra citati. Ma il refactoring del codice comune rende ugualmente difficile introdurre variazioni specifiche del prodotto, e porta a un sacco di "salto di classe" quando si deve trovare la linea di codice per correggere un bug - una modifica in una classe genitore di livello superiore potrebbe innescare i bug di regressione del trigger tra tutti i discendenti.

Come si ottiene un equilibrio ottimale tra DRY e accoppiamento di codice indesiderato?

    
posta user2549686 01.06.2018 - 08:47
fonte

3 risposte

8

lets say I refactored duplicate code into the "gasoline" hierarchy and the "electric" hierarchy, both descending from the "vehicle" hierarchy. So far so good.

And then, my company introduces a hybrid car and a hybrid Semi - that would require core changes to my original hierarchy itself. Maybe it would require "composition" between the gasoline and the electric hierarchies

Penso che questo sia uno dei motivi principali per cui le persone si stanno spostando verso la composizione sull'ereditarietà.

L'ereditarietà ti costringe a una grande ristrutturazione quando hai un cambiamento concettuale come quello che descrivi.

Quando la ristrutturazione è "troppo grande / difficile", le persone scrivono il codice duplicato per evitarlo.

Invece di eseguire il duplicato remoto spostando il codice nella catena di ereditarietà, è possibile spostarlo in una classe helper o servizio e quindi iniettare quella classe come parte di una composizione dove richiesto.

Se il progetto risultante è OOP è aperto al dibattito

    
risposta data 01.06.2018 - 12:26
fonte
4

Sei corretto, seguendo il principio di ASCIUTTO, puoi aumentare l'accoppiamento tra moduli altrimenti non correlati. Soprattutto nei sistemi software più grandi, questo può portare a situazioni in cui non seguire DRY può essere l'alternativa migliore.

Sfortunatamente, il tuo esempio non è adatto per dimostrarlo, i problemi descritti sono causati da errori classici nell'uso errato non ottimale dell'ereditarietà. Tuttavia, per quanto ho scritto sopra, non importa se si rifatta il codice comune in una classe base comune o in una classe helper (composizione). In entrambi i casi, si potrebbe essere costretti a mettere il codice comune in una libreria L e fare riferimento a quella libreria da due programmi precedentemente non correlati A e B.

Supponiamo che A e B fossero completamente estranei prima, possono essere versionati, rilasciati e distribuiti indipendentemente. Inserendo codice comune in una lib L condivisa, tuttavia, i nuovi requisiti per A possono indurre modifiche a L, che ora possono causare modifiche a B. Quindi questo causa la necessità di test aggiuntivi e probabilmente un nuovo ciclo di rilascio e distribuzione per B.

Quindi, come puoi gestire questa situazione, se non sei disposto ad abbandonare il principio DRY? Bene, ci sono alcune tattiche ben note per avvicinarsi a questo:

  1. Puoi mantenere A, B e L come parte dello stesso prodotto, con un numero di versione comune, un processo di compilazione, rilascio e distribuzione comune, con un alto grado di automazione

  2. o rendere L un prodotto a sé stante, con numeri di versione minori (senza modifiche incompatibili) e numeri di versione principali (magari contenenti modifiche di rottura) e lasciare che A e B consentano a ciascuno di fare riferimento a una diversa versione della linea di L.

  3. Rendi L il più possibile SOLIDO e cura la retrocompatibilità. Più moduli in L possono essere riutilizzati senza modifiche (OCP), meno probabile si verifichino cambiamenti di rottura. E gli altri principi di "SOLID" stanno contribuendo a sostenere questo obiettivo.

  4. Utilizza i test automatici soprattutto per L, ma anche per A e B.

  5. Fai attenzione a ciò che metti in L. La logica di business che dovrebbe esistere solo in un punto di un sistema è un buon candidato. Le cose che si "assomigliano" e potrebbero variare in futuro in modo diverso sono cattivi candidati.

Nota quando A e B sono sviluppati, mantenuti ed evoluti da diversi team indipendenti, il principio DRY diventa molto meno importante - DRY riguarda la manutenibilità e l'evolvibilità, ma lasciare che due diversi team forniscano uno sforzo di manutenzione individuale può essere talvolta più efficace di legando insieme i loro prodotti a causa di un po 'di riutilizzo.

Quindi alla fine, è un compromesso. Se si desidera seguire il principio DRY nei sistemi più grandi, è necessario investire molto più impegno nella creazione di componenti solidi e riutilizzabili, in genere più di quanto ci si aspetti. Devi fidarti del tuo giudizio quando ne vale la pena, e quando no.

    
risposta data 02.06.2018 - 09:23
fonte
2

DRY è un'altra regola strutturale. Ciò significa che se lo porti agli estremi è pessimo come se lo ignorassi. Ecco una metafora per tirarti fuori dalla mentalità strutturale.

Ama i tuoi gemelli. Uccidi i cloni.

Quando copi e incolli ciecamente per evitare di digitare la tastiera, crei cloni senza pensarci. Certo che fanno quello che vuoi, ma ti appesantiscono perché ora cambiare quel comportamento è così costoso.

Quando riproduci un comportamento identico con un codice identico a causa di una responsabilità diversa, ora hai lo stesso bisogno ma potresti sottoporli a cambiamenti diversi, hai un gemello che dovrebbe essere libero di cambiare e crescere come individuo col passare del tempo.

Puoi scansionare il loro DNA (codice) come preferisci. Cloni e gemelli sono difficili da distinguere se tutto ciò che fai è guardarli. Ciò di cui hai bisogno è capire il contesto in cui vivono. Perché sono nati. Quale sarà il loro destino finale.

Diciamo che lavori per la compagnia ABC. È gestito da tre dirigenti aziendali, A, B e C. Tutti vogliono che crei un prodotto software, ma ognuno di essi ha i propri reparti. Tutti vogliono che tu cominci piccolo. Per ora, manda un messaggio semplice. Quindi scrivi "Hello World".

A lo odia, vuole che tu inserisca il nome della compagnia lì.

B lo adora, vuole che tu lo lasci da solo e aggiungi il nome della società a una schermata iniziale.

C vuole solo una calcolatrice e pensava che il messaggio fosse solo un modo per iniziare.

Di una cosa di cui sei sicuro, questi ragazzi hanno idee molto diverse su cosa sia questo progetto. A volte stanno per essere d'accordo. A volte ti trascinano in direzioni diverse.

Quando duplichi il codice perché stai creando la capacità per quel codice di variare in modo indipendente, stai creando un gemello. Quando duplichi il codice perché scrivere è un dolore e copiare e incollare è facile, stai creando cloni malvagi.

A, B e C sono diversi maestri che il tuo codice deve servire. Nessuna riga di codice può servire più di un master a lungo.

    
risposta data 01.06.2018 - 16:13
fonte

Leggi altre domande sui tag