È possibile applicare a secco senza aumentare l'accoppiamento?

13

Supponiamo di avere un modulo software A che implementa una funzione F. Un altro modulo B implementa la stessa funzione di F '.

Esistono diversi modi per sbarazzarsi del codice duplicato:

  1. Consenti A usa F 'da B.
  2. Consenti a B di usare F da A.
  3. Inserisci F nel proprio modulo C e lascia che sia A sia B lo usino.

Tutte queste opzioni generano dipendenze aggiuntive tra i moduli. Applicano il principio DRY a costo di aumentare l'accoppiamento.

Per quanto posso vedere, l'accoppiamento è sempre aumentato o in leasing spostato a un livello più alto quando si applica DRY. Sembra esserci un conflitto tra due dei principi più basilari del design del software.

(In realtà non trovo sorprendente che ci siano conflitti del genere: è probabilmente questo a rendere così difficile la buona progettazione del software, ma trovo sorprendente che questi conflitti non siano normalmente affrontati nei testi introduttivi.)

Modifica (per chiarimenti): presumo che l'uguaglianza di F e F 'non sia solo una coincidenza. Se F dovrà essere modificato, F 'probabilmente dovrà essere modificato allo stesso modo.

    
posta Frank Puffer 15.07.2018 - 19:18
fonte

5 risposte

14

All of these options generate additional dependencies between modules. They apply the DRY principle at the cost of increasing coupling.

Perché sì, lo fanno. Ma diminuiscono l'accoppiamento tra le linee. Quello che ottieni è il potere di cambiare l'accoppiamento. L'accoppiamento si presenta in molte forme. Il codice di estrazione aumenta l'induzione e l'astrazione. Aumento che può essere buono o cattivo. La cosa numero uno che decide quale ottenere è il nome che usi per questo. Se guardare il nome mi lascia sorpreso quando guardo dentro, allora non hai fatto alcun favore a nessuno.

Inoltre, non seguire DRY nel vuoto. Se uccidi la duplicazione, ti stai assumendo la responsabilità di prevedere che questi due usi di quel codice cambieranno insieme. Se è probabile che cambino in modo indipendente, hai causato confusione e lavoro extra con poco beneficio. Ma un nome davvero buono può renderlo più appetibile. Se tutto quello che riesci a pensare è un brutto nome, per favore, fermati ora.

L'accoppiamento esiste sempre, a meno che il tuo sistema non sia così isolato che nessuno saprà mai se funziona. Quindi l'accoppiamento di refactoring è un gioco per scegliere il tuo veleno. Seguire DRY può ripagare riducendo al minimo l'accoppiamento creato esprimendo ripetutamente la stessa decisione di progettazione in molti punti finché non sarà molto difficile cambiare. Ma DRY può rendere impossibile capire il tuo codice. Il modo migliore per salvare questa situazione è trovare un buon nome. Se non riesci a pensare ad un buon nome, spero che tu sia esperto in evitando nomi privi di significato

    
risposta data 15.07.2018 - 21:58
fonte
3

Ci sono modi per rompere le dipendenze esplicite. Uno dei più popolari è quello di iniettare le dipendenze in fase di runtime. In questo modo, ottieni ASCIUTTO, rimuovi l'accoppiamento al costo della sicurezza statica. Al giorno d'oggi è così popolare che le persone non capiscono nemmeno, questo è un compromesso. Ad esempio, i contenitori di applicazioni forniscono di routine la gestione delle dipendenze, complicando enormemente il software nascondendo la complessità. Anche la semplice vecchia iniezione del costruttore non garantisce alcuni contratti a causa della mancanza del sistema di tipi.

Per rispondere al titolo - sì, è possibile, ma prepararsi alle conseguenze della spedizione in runtime.

  • Definisci l'interfaccia F A in A, fornendo funzionalità di F
  • Definisci l'interfaccia F B in B
  • Inserisci F in C
  • Crea il modulo D per gestire tutte le dipendenze (dipende da A, B e C)
  • Adatta F a F A e F B a D
  • Inject (passa) wrapper in A e B

In questo modo, l'unico tipo di dipendenze che dovresti avere è D in base a ciascun altro modulo.

Oppure registra C nel contenitore delle applicazioni con un'integrazione delle dipendenze incorporata e goditi le potenzialità di autowiring in fase di runtime, aumentando lentamente la velocità di caricamento dei loop e dei deadlock.

    
risposta data 15.07.2018 - 21:30
fonte
1

Non sono sicuro che una risposta senza ulteriore contesto abbia un senso.

A dipende già da B o viceversa? - nel qual caso potremmo avere una scelta ovvia di casa per F .

Do A e B già condividono tutte le dipendenze comuni che potrebbero essere una buona casa per F ?

Quanto grande / complesso è F ? Che altro dipende da F ?

I moduli A e B sono utilizzati nello stesso progetto?

A e B finiranno comunque per condividere qualche dipendenza comune?

Quale linguaggio / sistema di moduli viene utilizzato: quanto è doloroso un nuovo modulo, nel dolore del programmatore, nelle prestazioni generali? Ad esempio, se stai scrivendo in C / C ++ con il sistema modulo come COM, che causa dolore al codice sorgente, richiede strumenti alternativi, ha implicazioni sul debug e ha implicazioni sulle prestazioni (per le chiamate tra moduli), potrei prendi una pausa seria.

D'altro canto, se stai parlando di DLL Java o C # che si combinano piuttosto perfettamente in un unico ambiente di esecuzione, è un'altra questione.

Una funzione è un'astrazione e supporta DRY.

Tuttavia, devono essere complete delle buone astrazioni: le astrazioni incomplete possono benissimo far sì che il consumatore consumatore (programmatore) compensi la carenza usando la conoscenza dell'implementazione sottostante: questo si traduce in un accoppiamento più stretto rispetto a se l'astrazione fosse offerta come più completa.

Quindi, direi di cercare di creare un'astrazione migliore per A e B su cui contare, piuttosto che spostare semplicemente una singola funzione in un nuovo modulo C .

Sarei alla ricerca di una serie di funzioni per dare una nuova astrazione, vale a dire, potrei aspettare fino a quando il codice base sarà più avanti per identificare un refactoring di astrazione più completo / completo da fare piuttosto di uno basato su un singolo codice di funzione tell.

    
risposta data 15.07.2018 - 21:29
fonte
0

Le risposte qui che si concentrano su tutti i modi in cui è possibile "minimizzare" questo problema ti stanno rendendo un disservizio. E le "soluzioni" che offrono semplicemente diversi modi per creare un accoppiamento non sono assolutamente soluzioni.

La verità è che non riescono a capire lo stesso problema che hai creato. Il problema con il tuo esempio non ha nulla a che vedere con DRY, piuttosto (più in generale) con il design dell'applicazione.

Chiediti perché i moduli A e B sono separati se entrambi dipendono dalla stessa funzione F? Ovviamente avrai problemi con la gestione delle dipendenze / l'astrazione / l'accoppiamento / il tuo nome se ti impegni a un design scadente.

La corretta modellazione delle applicazioni viene eseguita in base al comportamento. Pertanto, i pezzi di A e B che dipendono da F devono essere estratti nel loro modulo indipendente. Se questo non è possibile, allora A e B devono essere combinati. In entrambi i casi, A e B non sono più utili al sistema e dovrebbero cessare di esistere.

DRY è un principio che può essere utilizzato per esporre un design scadente, non per causarlo. Se non riesci a ottenere DRY ( quando si applica veramente - notando la tua modifica) a causa della struttura della tua applicazione, è un chiaro segno che la struttura è diventata una responsabilità. Questo è il motivo per cui "il refactoring continuo" è anche un principio da seguire.

Gli ABC di altri principi di progettazione (SOLID, DRY, ecc.) sono tutti usati per rendere modificare (includendo il refactoring) un'applicazione più indolore. Concentrati su che e tutti gli altri problemi iniziano a scomparire.

    
risposta data 16.07.2018 - 02:08
fonte
0

All of these options generate additional dependencies between modules. They apply the DRY principle at the cost of increasing coupling.

Ho un'opinione diversa, almeno per la terza opzione:

Dalla tua descrizione:

  • A ha bisogno di F
  • B ha bisogno di F
  • Né A né B hanno bisogno l'uno dell'altro.

L'inserimento di F in un modulo C non aumenta l'accoppiamento poiché sia A sia B hanno già bisogno della funzionalità di C.

    
risposta data 24.07.2018 - 10:14
fonte

Leggi altre domande sui tag