Che cos'è un livello anticorruzione e come viene utilizzato?


151

Sto cercando di capire cosa significhi davvero il livello anticorruzione. So che è un modo per passare / aggirare il codice legacy o API errate. Quello che non capisco è come funziona e ciò che lo rende una separazione netta dallo strato indesiderabile.

Ho fatto qualche ricerca, ma non riesco a trovare semplici esempi o spiegazioni, quindi sto cercando qualcuno che lo capisca e possa spiegarlo con semplici esempi. Una risposta che soddisfi la mia domanda dovrebbe essere semplice (non necessariamente breve) e fornire esempi comprensibili di implementazione e utilizzo.

Vedi questa domanda , per il mio caso d'uso.

Risposte:


147

Immagina di dover usare il codice di qualcun altro progettato come mostrato di seguito:

    class Messy {
        String concat(String param, String str) { /* ... */ }
        boolean contains(String param, String s) { /* ... */ }
        boolean isEmpty(String param) { /* ... */ }
        boolean matches(String param, String regex) { /* ... */ }
        boolean startsWith(String param, String prefix) { /* ... */ }
    }

Ora immagina di scoprire che il tuo codice che dipende da esso è simile al seguente:

String process(String param) {
    Messy messy = new Messy();
    if (messy.contains(param, "whatever")) {
        return messy.concat(param, "-contains");
    }
    if (messy.isEmpty(param)) {
        return messy.concat(param, "-empty");
    }
    if (messy.matches(param, "[whatever]")) {
        return messy.concat(param, "-matches");
    }
    if (messy.startsWith(param, "whatever")) {
        return messy.concat(param, "-startsWith");
    }
    return messy.concat(param, "-whatever");
    // WTF do I really need to repeat bloody "param" 9 times above?
}

... e che si desidera semplificare l'utilizzo, in particolare, per eliminare l'uso ripetitivo di parametri che non sono necessari per l'applicazione.

Bene, allora inizi a costruire un livello anticorruzione.

  1. La prima cosa è assicurarsi che il "codice principale" non si riferisca Messydirettamente. Ad esempio, si organizza la gestione delle dipendenze in modo tale che il tentativo di accedere Messynon riesca a compilare.

  2. In secondo luogo, crei un modulo "layer" dedicato che è l'unico ad accedervi Messyed esponilo al tuo "codice principale" in un modo che abbia più senso per te.

Il codice del livello sarebbe simile al seguente:

    class Reasonable { // anti-corruption layer
        String param;
        Messy messy = new Messy();
        Reasonable(String param) {
            this.param = param;
        }
        String concat(String str) { return messy.concat(param, str); }
        boolean contains(String s) { return messy.contains(param, s); }
        boolean isEmpty() { return messy.isEmpty(param); }
        boolean matches(String regex) { return messy.matches(param, regex); }
        boolean startsWith(String prefix) { return messy.startsWith(param, prefix); }
    }

Di conseguenza, il tuo "codice principale" non si scherza Messy, usando Reasonableinvece, come segue:

String process(String param) {
    Reasonable reasonable = new Reasonable(param);
    // single use of "param" above and voila, you're free
    if (reasonable.contains("whatever")) {
        return reasonable.concat("-contains");
    }
    if (reasonable.isEmpty()) {
        return reasonable.concat("-empty");
    }
    if (reasonable.matches("[whatever]")) {
        return reasonable.concat("-matches");
    }
    if (reasonable.startsWith("whatever")) {
        return reasonable.concat("-startsWith");
    }
    return reasonable.concat("-whatever");
}

Nota che c'è ancora un po 'di confusione, Messyma ora è nascosto ragionevolmente in profondità Reasonable, rendendo il tuo "codice principale" ragionevolmente pulito e privo di corruzione che verrebbe portato lì dall'uso diretto di Messycose.


L'esempio sopra si basa su come viene spiegato il livello di anticorruzione nel wiki di c2:

Se l'applicazione deve gestire un database o un'altra applicazione il cui modello è indesiderabile o inapplicabile al modello desiderato all'interno della propria applicazione, utilizzare un AnticorruptionLayer per tradurre da / verso quel modello e il proprio.

Nota esempio è intenzionalmente reso semplice e condensato per mantenere la spiegazione breve.

Se hai un più ampio mess-of-API da coprire dietro il livello anticorruzione, si applica lo stesso approccio: in primo luogo, assicurati che il tuo "codice principale" non acceda direttamente alle cose danneggiate e in secondo luogo, esponilo in un modo che sia più conveniente nel contesto di utilizzo.

Quando "ridimensionate" il livello oltre un esempio semplificato sopra, tenete presente che rendere conveniente la vostra API non è necessariamente un'attività banale. Investi uno sforzo per progettare il tuo livello nel modo giusto , verificane l'uso previsto con unit test ecc.

In altre parole, assicurati che la tua API sia effettivamente un miglioramento rispetto a quella che nasconde, assicurati di non introdurre solo un altro livello di corruzione.


Per completezza, notare una differenza sottile ma importante tra questo e i relativi schemi Adapter and Facade . Come indicato dal suo nome, il livello anticorruzione presuppone che l' API sottostante presenti problemi di qualità (è "corrotto") e intende offrire una protezione dei problemi citati.

Puoi pensarlo in questo modo: se puoi giustificare che il progettista di biblioteche starebbe meglio esponendo la sua funzionalità con Reasonableinvece di Messy, ciò significherebbe che stai lavorando su un livello anticorruzione, facendo il loro lavoro, correggendo i loro errori di progettazione.

Al contrario, Adapter e Facade non fanno ipotesi sulla qualità del design sottostante. Questi potrebbero essere applicati all'API che è ben progettata per cominciare, adattandola alle tue esigenze specifiche.

In realtà, potrebbe anche essere più produttivo supporre che modelli come Adapter e Facade si aspettino che il codice sottostante sia ben progettato. Puoi pensarlo in questo modo: un codice ben progettato non dovrebbe essere troppo difficile da modificare per un caso d'uso particolare. Se si scopre che la progettazione dell'adattatore richiede più sforzo del previsto, ciò potrebbe indicare che il codice sottostante è, in qualche modo, "corrotto". In tal caso, puoi considerare di suddividere il lavoro in fasi separate: in primo luogo, stabilisci un livello anticorruzione per presentare l'API sottostante in modo adeguatamente strutturato e, successivamente, disegna l'adattatore / la facciata su quel livello di protezione.


1
In che modo questa scala se esiste un'intera struttura di classi API dipendenti? È ancora più gestibile del livello da cui protegge il resto dell'applicazione?
knownasilya,

1
@Knownasilya è un'ottima domanda, la risposta è stata estesa per risolverlo
moscerino del

4
In other words, make sure that your API is indeed an improvement over one it hides, make sure that you don't just introduce another layer of corruption.L'intera sezione è degna di un tag in grassetto.
Lilienthal,

19
I livelli anticorruzione non hanno nulla a che fare con la gestione di API di scarsa qualità. Si tratta di gestire discrepanze concettuali, adattando domini che potremmo usare solo "corrompendo" il nostro codice su domini che possiamo usare più facilmente.
Ian Fairman,

8
Ian Fairman ha capito bene, mentre l'autore di questa risposta sicuramente no. Se vai alla fonte del concetto (il libro DDD), troverai almeno due cose che contraddicono questa risposta: 1) viene creato uno strato anticorruzione per evitare di corrompere il nuovo modello di dominio che stiamo sviluppando con elementi dal modello di un sistema esterno esistente; non è che l'altro sistema sia "corrotto", in effetti potrebbe essere perfettamente buono e ben progettato; 2) uno strato anticorruzione di solito conterrà diverse classi, spesso comprese facciate e adattatori , nonché servizi .
Rogério,

41

Per citare un'altra fonte:

Creare un livello di isolamento per fornire ai client funzionalità in termini di modello di dominio. Il layer comunica con l'altro sistema attraverso la sua interfaccia esistente, richiedendo poche o nessuna modifica all'altro sistema. Internamente, il livello si traduce in entrambe le direzioni secondo necessità tra i due modelli.

Eric Evans, Domain Driven Design, 16a stampa, pagina 365

La cosa più importante è che termini diversi sono usati su ciascun lato del livello anticorruzione. Una volta stavo lavorando su un sistema per la logistica dei trasporti. I round dovevano essere pianificati. Dovevi equipaggiare il veicolo in un deposito, guidare verso diversi siti di clienti e sottoporli a manutenzione e visitare altri luoghi, come una fermata del serbatoio. Ma dal livello più alto tutto riguardava la pianificazione delle attività. Quindi aveva senso separare i termini di pianificazione delle attività più generali dai termini logistici di trasporto molto specifici.

Quindi un isolamento anti-corruzione non riguarda solo la protezione da codice disordinato, ma separa domini diversi e si assicura che rimangano separati in futuro.


6
Questo è molto importante! Un ACL non deve essere utilizzato solo con il codice disordinato, ma come mezzo per comunicare tra contesti limitati. Si traduce da un contesto all'altro, in modo che i dati in ciascun contesto riflettano la lingua e il modo in cui quel contesto pensa e parla dei dati.
Didier A.

29

Adattatore

Quando si hanno interfacce incompatibili, che eseguono una logica simile, per adattarsi l'una all'altra, in modo da poter usare le implementazioni di uno con cose che si aspettano l'altro.

Esempio:

Hai un oggetto che vuole un'auto, ma hai solo una classe 4WheelVehicle, quindi crei un CarBuiltUsing4WheelVehicle e lo usi come auto.

Facciata

Quando hai un'API complessa / confusa / gigantesca e vuoi renderla più semplice / più chiara / più piccola. Creerai una facciata per nascondere la complessità / confusione / extra ed esporre solo una nuova API semplice / chiara / piccola.

Esempio:

Stai usando una libreria che ha 100 metodi e per eseguire determinate attività devi fare un sacco di inizializzazione, connessione, apertura / chiusura delle cose, solo per essere finalmente in grado di fare quello che volevi, e tutto ciò che volevi è 1 funzione di tutte le 50 librerie possono fare, quindi crei una facciata che ha solo un metodo per quella 1 funzione di cui hai bisogno e che fa tutto l'inizializzazione, la pulizia, ecc. per te.

Strato anticorruzione

Quando disponi di un sistema che non rientra nel tuo dominio, tuttavia le esigenze della tua azienda richiedono di lavorare con quell'altro dominio. Non vuoi introdurre questo altro dominio nel tuo, quindi corromperlo, quindi tradurrai il concetto del tuo dominio, in questo altro dominio e viceversa.

Esempio:

Un sistema visualizza il cliente con un nome e un elenco di stringhe, uno per ogni transazione. I profili vengono visualizzati come classi autonome con un nome e Transazioni come classi autonome con una stringa e il cliente come profilo e una raccolta di transazioni.

Quindi crei un livello ACL che ti permetterà di tradurre tra il tuo cliente e il cliente dell'altro sistema. In questo modo, non devi mai usare il cliente dell'altro sistema, devi semplicemente dire all'ACL: "dammi il cliente con il profilo X e l'ACL dice all'altro sistema di assegnargli un nome di nome X. e restituisce tu un cliente con il profilo X.

====================

Tutti e tre sono relativamente simili, perché sono tutti modelli di riferimento indiretto. Ma si rivolgono a diverse strutture, classi / oggetti contro API rispetto a moduli / sottosistemi. Potresti averli tutti combinati, se necessario. Il sottosistema ha un'API complessa, quindi costruisci una FACCIATA per esso, utilizza un modello diverso, quindi per ogni rappresentazione di dati che non si adatta al tuo modello, dovresti TRADURRE i dati nel modo in cui li modelli. Infine, forse anche le interfacce sono incompatibili, quindi dovresti usare ADATTATORI per adattarti dall'uno all'altro.


12

Molte risposte qui affermano che gli ACL "non solo" riguardano il wrapping del codice disordinato. Andrei oltre e direi che non lo riguardano affatto, e se lo fanno allora è un vantaggio collaterale.

Un livello anticorruzione riguarda la mappatura di un dominio su un altro in modo che i servizi che utilizzano il secondo dominio non debbano essere "danneggiati" dai concetti del primo. Gli ACL sono per i modelli di dominio ciò che gli adattatori sono per le classi, sta accadendo solo a un livello diverso. L'adattatore è probabilmente il modello di progettazione più importante - lo uso sempre - ma giudicare la classe spostata come disordinata o meno è irrilevante. È quello che è, ho solo bisogno che abbia un'interfaccia diversa.

Concentrarsi sul disordine è fuorviante e manca il punto su ciò che è DDD. Gli ACL si occupano di disallineamenti concettuali, non di scarsa qualità.

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.