Raggiungere il disaccoppiamento nelle classi del modello

6

Sto provando a testare (o almeno scrivere test di unità) sulle mie classi Model ma ho notato che le mie classi finiscono per essere troppo accoppiate. Poiché non riesco a rompere questo accoppiamento, scrivere test di unità sta diventando sempre più difficile.

Per essere più specifici:

Classi di modelli: sono le classi che contengono i dati nella mia applicazione. Assomigliano più o meno al POJO (semplici vecchi oggetti Java), ma hanno anche alcuni metodi. L'applicazione non è troppo grande quindi ho circa 15 classi di modelli.

Accoppiamento : solo per fare un esempio, pensa a un semplice caso di Order Header - > Ordine Articolo. L'intestazione conosce l'elemento e l'elemento conosce l'intestazione (richiede alcune informazioni dall'intestazione per l'esecuzione di determinate operazioni). Quindi, diciamo che esiste la relazione tra l'articolo dell'ordine - > Rapporto sull'articolo Il report dell'articolo ha bisogno anche dell'oggetto. A questo punto, immagina di scrivere test per il report degli articoli; è necessario avere un'intestazione dell'ordine per eseguire i test. Questo è un caso semplice con 3 classi; le cose si complicano con più classi.

Posso creare classi disaccoppiate quando disegno algoritmi, livelli di persistenza, interazioni utente, ecc ... ma con le classi modello, non riesco a pensare a un modo per separarli. Attualmente siedono come una grande fetta di classi che dipendono l'una dall'altra.

Ecco alcuni accorgimenti che posso pensare:

  1. Generatori di dati : ho un pacchetto che genera dati di esempio per le mie classi di modelli. Ad esempio, la classe OrderHeaderGenerator crea OrderHeaders con alcuni dati di base. Uso OrderHeaderGenerator dai miei unit-test ItemReport in modo da ottenere un'istanza sulla classe OrderHeader. Il problema è che questi generatori si complicano molto velocemente e quindi ho bisogno di testare questi generatori; sconfiggere un po 'lo scopo.
  2. Interfacce anziché dipendenze : posso creare interfacce per sbarazzarsi delle durissime dipendenze. Ad esempio, la classe OrderItem dipende dall'interfaccia IOrderHeader. Quindi, nei miei test unitari, posso facilmente prendere in giro il comportamento di un OrderHeader con una classe FakeOrderHeader che implementa l'interfaccia IOrderHeader. Il problema con questo approccio è la complessità che le classi del modello finirebbero per avere.

Avresti altre idee su come rompere questo accoppiamento nelle classi modello? Oppure, come rendere più semplice il test unitario delle classi del modello?

Aggiornamento dopo aver esaminato le risposte:

  • Ho riflettuto un po 'su questo problema e ho notato che alcune di queste dipendenze non erano realmente necessarie (come sottolineato dalla maggior parte delle risposte). Il motivo per cui esistevano è perché il mio livello di interfaccia utente ne aveva bisogno. Fondamentalmente, ho inserito queste dipendenze in modo che il livello dell'interfaccia utente possa facilmente navigare da bambino all'intestazione e visualizzare alcuni campi di intestazione nelle viste a livello figlio. Guardandolo ora, sembra così semplice; tuttavia mi ci è voluto del tempo per capire perché queste dipendenze esistevano in primo luogo. Ora, l'interfaccia utente manterrà fondamentalmente l'intestazione nel suo contesto durante la navigazione verso le visualizzazioni a livello figlio.
  • D'altra parte, ho rimosso alcune delle dipendenze tramite interfacce. Queste dipendenze erano principalmente one-to-one; per esempio tra Header e HeaderReport. In precedenza, HeaderReport dipendeva dall'intestazione per il recupero delle informazioni (elementi, data, ecc.) E ciò rendeva molto difficile testare HeaderReport. Ora, HeaderReport dipende dall'interfaccia HeaderDataProvider e sono in grado di ottenere l'isolamento completo. HeaderReport è molto facile da testare con le classi stub per l'interfaccia HeaderDataProvider.
  • Sembra facile creare queste astrazioni (UI Layer - Model Layer - Backend Layer), tuttavia in realtà ci vuole molta pratica!
posta Guven 08.10.2012 - 22:11
fonte

4 risposte

5

Le classi hanno davvero bisogno di essere disaccoppiate? Nel tuo esempio, dal punto di vista del business, l'intestazione dell'ordine e l'ordine sono entità, che sono molto vicine. Probabilmente formano un aggregato . Quindi, cercare di separare quelli creerà una complessità inutile e renderà la lettura del modello confusa.

Per risolvere il problema, proverei a individuare gli aggregati nel codice e quindi a testare gli aggregati interi.

    
risposta data 08.10.2012 - 22:22
fonte
6

So che stai cercando di semplificare il tuo scenario per renderlo più facile da discutere, ma perché OrderItem si preoccupa dell'intestazione? L'intestazione è Aggregator (chiamato "Root" in DDD) ed è responsabile della conoscenza dei suoi componenti (OrderItem è uno di essi).

Se stai discutendo casi speciali come gli sconti applicati agli ordini contenenti per esempio 10 o più di un determinato articolo, sarebbe un lavoro di dire un servizio di sconti per trovare gli sconti applicabili mentre l'ordine è in fase di elaborazione.

La stessa cosa si applicherebbe a ItemReport ... è responsabile conoscere le regole su come gli oggetti sono selezionati, filtrati, ordinati, ecc. L'Articolo non dovrebbe preoccuparsi di riportare, non è il suo lavoro.

    
risposta data 08.10.2012 - 22:50
fonte
1

Would you have other ideas on how to break this coupling in the model classes? Or, how to make it easier to unit-test the model classes?

Secondo me, la codifica contro le interfacce è la strada da percorrere. In altre parole, disaccoppiare il codice in base alla progettazione è l'approccio seguito dai progetti moderni. Ciò è di grande aiuto con le esigenze di unit-testing .

In altre parole, si consiglia di definire le specifiche di interfaccia formali, precise e verificabili per i componenti software che si prevede di sviluppare o tentare di rimuovere dalla coppia per rimuovere le dipendenze.

    
risposta data 08.10.2012 - 22:28
fonte
1

Un modo per farlo è, come hai detto, creare interfacce tra le classi. Ogni classe prenderebbe le sue dipendenze come riferimenti di interfaccia che possono essere derisi o soppressi in un contesto di test unitario. Questo potrebbe farti pensare un po 'di più sulle parti essenziali del design che sono necessarie per ogni classe. Ad esempio, potresti scoprire che l'articolo dell'ordine non ha bisogno dell'intestazione completa dell'ordine. Questo processo potrebbe portare a scoprire concetti latenti nella progettazione, che aumentano le possibilità di riutilizzo e creano elementi più in linea con il loro intento, o raison d'être .

D'altra parte, forse la creazione di interfacce sembra strana per concetti così sinergici e accoppiati. In questo caso, puoi utilizzare generatori di dati di test fluenti per creare un dominio specifico e un modo diretto per creare facilmente dipendenti oggetti. Non è necessario testare direttamente questi builder, poiché verranno utilizzati dal codice di test per creare gli oggetti dipendenti.

    
risposta data 09.10.2012 - 03:20
fonte

Leggi altre domande sui tag