L'accoppiamento del codice viene introdotto da DRY e OOD


14

Sto cercando una guida sull'accoppiamento DRY vs Code. Non mi piace duplicare il mio codice e non mi piace nemmeno l'accoppiamento del codice tra moduli non correlati. Quindi refactoring codice duplicato se trovo codice identicamente duplicato un anno dopo l'introduzione della duplicazione. Tuttavia, ho sperimentato sempre più situazioni in cui il mondo reale è molto più imprevedibile e, dopo il refactoring del codice, sorgono situazioni che richiedono di nuovo il bucato del codice.

Ad esempio, se avessi il codice per gestire le auto a benzina, i SUV a benzina, le auto elettriche e i SUV elettrici, diciamo che ho modificato il codice duplicato nella gerarchia "benzina" e nella gerarchia "elettrica", entrambe discendenti dalla gerarchia "veicolo". Fin qui tutto bene. E poi, la mia azienda introduce un'auto ibrida e una Semi ibrida, che richiederebbe cambiamenti fondamentali nella mia stessa gerarchia originale. Forse richiederebbe una "composizione" tra la benzina e le gerarchie elettriche.

Chiaramente la duplicazione del codice non è buona perché aumenta il tempo impiegato per implementare una modifica comune a tutti i prodotti di cui sopra. Ma il refactoring del codice comune rende altrettanto difficile introdurre variazioni specifiche del prodotto e porta a un sacco di "salto di classe" quando si deve trovare la riga di codice per correggere un bug - un cambiamento in una classe genitore di livello superiore potrebbe innescare innescare bug di regressione tra tutti i discendenti.

Come si fa a trovare un equilibrio ottimale tra DRY e accoppiamento indesiderato del codice?


1
È sempre bene non seguire le regole alla cieca, ma coinvolgere prima il cervello.
gnasher729,

abbastanza giusto - quindi linee guida, non regole.
user2549686

Hai letto la pagina Wiki su DRY (o OAOO, come si chiamava un tempo)? wiki.c2.com/?OnceAndOnlyOnce Ecco dove sono avvenute molte discussioni iniziali su di esso. (In realtà, Ward ha inventato il Wiki appositamente per le discussioni su Patterns and Practices.)
Jörg W Mittag

Risposte molto correlate, anche se la domanda non sembra del tutto simile a un duplicato: softwareengineering.stackexchange.com/questions/300043/…
Hulk

Risposte:


8

diciamo che ho modificato il codice duplicato nella gerarchia "benzina" e nella gerarchia "elettrica", entrambe discendenti dalla gerarchia "veicolo". Fin qui tutto bene.

E poi, la mia azienda introduce un'auto ibrida e una Semi ibrida, che richiederebbe cambiamenti fondamentali nella mia stessa gerarchia originale. Forse richiederebbe una "composizione" tra la benzina e le gerarchie elettriche

Penso che questo sia uno dei motivi principali per cui le persone si muovono verso la composizione piuttosto che sull'eredità.

L'ereditarietà ti costringe a grandi ristrutturazioni quando hai un cambiamento concettuale come quello che descrivi.

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

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

Se il design risultante è OOP è aperto al dibattito


1
+1 per indicare che l'ereditarietà è il problema, non SECCO. Il modo più semplice per riutilizzare il codice è rimuovere le dipendenze non necessarie, e troppo spesso alla gente manca il fatto che una classe genitore (e i suoi discendenti) siano tutte dipendenze quando si usa l'ereditarietà. Solo quando riduci o elimini le dipendenze ottieni effettivamente codice riutilizzabile.
Greg Burghardt,

3
" Se il design risultante è OOP è aperto al dibattito ". Tutto è aperto al dibattito. La domanda da porsi è: è aperta a un dibattito ragionevole e razionale? La risposta è no. Penso che il tuo ultimo paragrafo toglie una risposta altrimenti molto buona.
David Arno,

@davidarno non ti segue davvero, ho letto OOD nel titolo come design orientato agli oggetti? quindi sto affrontando la questione senza trascinare nel dibattito
Ewan,

1
@Ewan: sono qui con David Arno. Penso che sia noto da più di 2 decenni che credere "se non c'è eredità, allora non è OOP" è un errore.
Doc Brown,

Accidenti, questo è esattamente il tipo di discussione che stavo cercando di evitare. ci sono opinioni da entrambe le parti, non c'è una risposta definitiva
Ewan,

3

Hai ragione, seguendo il principio DRY, puoi aumentare l'accoppiamento tra moduli altrimenti non correlati. Soprattutto nei sistemi software più grandi, ciò può portare a situazioni in cui non seguire DRY può essere la migliore alternativa.

Sfortunatamente, il tuo esempio non è adatto a dimostrarlo: i problemi descritti lì sono causati da errori classici nell'uso non ottimale dell'ereditarietà. Tuttavia, per quello che ho scritto sopra, non importa se si converte 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 siano stati completamente indipendenti prima, possono essere sottoposti a versione, rilasciati e distribuiti in modo indipendente. Inserendo un codice comune in una lib condivisa L, tuttavia, i nuovi requisiti per A possono indurre modifiche a L, che ora possono causare modifiche a B. Quindi ciò comporta la necessità di ulteriori test 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. Mantenere A, B e L come parte dello stesso prodotto, con un numero di versione comune, un processo comune di generazione, rilascio e distribuzione, con un elevato grado di automazione

  2. o rendere L un prodotto a sé stante, con numeri di versione minori (nessuna modifica incompatibile) e numeri di versione principali (forse contenenti modifiche di rottura), e lasciare che A e B consentano a ciascuno di fare riferimento a una diversa versione di L.

  3. Rendi L il più SOLIDO possibile e prenditi cura della compatibilità con le versioni precedenti. Più moduli in L possono essere riutilizzati senza modifiche (OCP), meno si verificheranno cambiamenti di rottura. E gli altri principi di "SOLID" stanno contribuendo a sostenere tale obiettivo.

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

  5. Fai attenzione a ciò che metti in L. La logica aziendale che dovrebbe esistere solo in un posto in un sistema è un buon candidato. Le cose che "si assomigliano" e potrebbero variare in modo diverso in futuro sono cattive candidate.

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

Quindi, alla fine, è un compromesso. Se vuoi seguire il principio DRY in sistemi più grandi, devi investire molti più sforzi nella creazione di componenti robusti e riutilizzabili, in genere più di quanto ti aspetti. Devi fidarti del tuo giudizio quando ne vale la pena e quando no.


1

DRY è un'altra regola strutturale. Ciò significa che se lo porti agli estremi è un male come se lo ignori. Ecco una metafora per farti uscire dalla mentalità strutturale.

Adoro i tuoi gemelli. Uccidi i cloni.

Quando copi e incolli ciecamente per evitare di digitare la tastiera, crei cloni senza pensarci. Certo 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 diversa responsabilità, sembra che ora abbia lo stesso bisogno, ma che potrebbe comportare 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à probabilmente il loro destino finale.

Diciamo che lavori per la compagnia ABC. È gestito da tre dirigenti aziendali, A, B e C. Tutti vogliono che tu faccia un prodotto software ma ognuno ha i propri reparti. Vogliono tutti che inizi in piccolo. Ho appena emesso un semplice messaggio per ora. Quindi scrivi "Hello World".

A la odia, vuole che tu ci metta il nome dell'azienda.

B lo adora, vuole che tu lo lasci in pace e aggiunga il nome dell'azienda a una schermata iniziale.

C vuole solo una calcolatrice e pensa che il messaggio sia solo un modo per iniziare.

Una cosa di cui sei sicuro, questi ragazzi hanno idee molto diverse su questo progetto. A volte sono d'accordo. A volte ti attireranno in diverse direzioni.

Quando duplicate il codice perché state creando la possibilità per quel codice di variare in modo indipendente, state creando un gemello. Quando si duplica il codice perché digitare è una seccatura e copiare e incollare è facile si creano cloni malvagi.

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


Innanzitutto, molte grazie a tutti, per tutti i commenti.
user2549686
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.