Che cos'è un livello Anti-corruzione e come viene utilizzato?

138

Sto cercando di capire cosa significhi realmente lo strato anti-corruzione. So che è un modo di transizione / aggirare il codice legacy o le API cattive. Quello che non capisco è come funziona e cosa rende una separazione netta dal livello indesiderabile.

Ho fatto qualche ricerca, ma non riesco a trovare esempi o spiegazioni semplici, quindi cerco 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.

    
posta knownasilya 22.01.2013 - 17:14
fonte

4 risposte

138

Immagina di dover utilizzare 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 vuoi rendere più facile l'utilizzo, in particolare, per sbarazzarti dell'uso ripetitivo di parametri che non sono necessari per la tua applicazione.

Ok, quindi inizi a creare un livello anti-corruzione.

  1. Per prima cosa assicurati che il tuo "codice principale" non si riferisca direttamente a Messy . Ad esempio, organizzi gestione delle dipendenze in modo che provi ad accedere a Messy non riesce a compilare.

  2. In secondo luogo, crei un modulo "layer" dedicato che è l'unico che accede a Messy e lo espone al tuo "codice principale" in un modo che ha più senso per te.

Il codice del layer sarà 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 incasina con Messy , usando invece Reasonable , 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");
}

Notate che c'è ancora un po 'di confusione con Messy , ma questo è ora nascosto ragionevolmente all'interno di Reasonable , rendendo il vostro "codice principale" ragionevolmente pulito e privo di corruzione che sarebbe portato lì tramite l'uso diretto di Messy stuff.

L'esempio sopra è basato su come Anticorruption Layer è spiegato su wiki c2:

If your application needs to deal with a database or another application whose model is undesirable or inapplicable to the model you want within your own application, use an AnticorruptionLayer to translate to/from that model and yours.

L'esempio di nota è intenzionalmente semplificato e condensato per mantenere una spiegazione breve.

Se hai un mess-of-API più grande per coprire il livello anti-corruzione, si applica lo stesso approccio: in primo luogo, assicurati che il tuo "codice principale" non acceda direttamente a roba corrotta in secondo luogo, esponilo in un modo che è più conveniente nel tuo contesto di utilizzo.

Quando "ridimensiona" il tuo livello al di là di un esempio semplificato sopra, considera che rendere conveniente la tua API non è necessariamente un compito banale. Investi un sforzo a progetta il tuo livello nel modo giusto , verificane la sua uso previsto con unit test ecc.

In altre parole, assicurati che la tua API sia effettivamente un miglioramento su una che nasconde, assicurati di non introdurre solo un altro livello di danneggiamento.

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

Puoi pensarci in questo modo: se puoi giustificare che il progettista di librerie sarebbe meglio esporre le sue funzionalità con Reasonable invece di Messy , questo significherebbe che stai lavorando sul livello di anticorruzione, facendo il loro lavoro , che fissa i loro errori di progettazione.

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

In realtà, potrebbe anche essere più produttivo assumere che pattern come Adapter e Facade si aspettino che il codice sottostante sia ben progettato. Si può pensare in questo modo: il codice ben progettato non dovrebbe essere troppo difficile da modificare per casi d'uso particolari. Se si scopre che la progettazione dell'adattatore richiede uno sforzo maggiore del previsto, ciò potrebbe indicare che il codice sottostante è, beh, in qualche modo "danneggiato". In tal caso, puoi considerare la suddivisione del lavoro in fasi separate: in primo luogo, stabilire un livello di anticorruzione per presentare l'API sottostante in modo adeguatamente strutturato e successivamente, progettare l'adattatore / facciata su quel livello di protezione.

    
risposta data 22.01.2013 - 18:19
fonte
34

Per citare un'altra fonte:

Create an isolating layer to provide clients with functionality in terms of their own domain model. The layer talks to the other system through its existing interface, requiring little or no modification to the other system. Internally, the layer translates in both directions as necessary between the two models.

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

La cosa più importante è che termini diversi vengono utilizzati su ciascun lato del livello anti corruzione. Una volta stavo lavorando su un sistema per il trasporto logistico. Rounds doveva essere pianificato. Dovevi attrezzare il veicolo in un deposito, guidare verso diversi siti di clienti e ripararli e visitare altri luoghi, come una fermata del carro armato. Ma dal livello superiore si trattava di pianificazione delle attività. Quindi aveva senso separare i termini più generali di pianificazione delle attività dai termini logistici di trasporto molto specifici.

Quindi un isolamento anti-corruzione dei livelli non serve solo a proteggerti dal codice disordinato, bensì a separare domini diversi e assicurarti che rimangano separati in futuro.

    
risposta data 26.01.2013 - 16:52
fonte
28

Adapter

Quando hai interfacce incompatibili, che eseguono una logica simile, per adattarne una all'altra, in modo da poter utilizzare le implementazioni di una con le cose che si aspettano l'altra.

Esempio:

Hai un oggetto che vuole una macchina, ma hai solo una classe 4WheelVehicle, quindi crei un CarBuiltUsing4WheelVehicle e utilizzalo come la tua 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 e esporrà solo una nuova API semplice / chiara / piccola.

Esempio:

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

Strato anticorruzione

Se disponi di un sistema fuori dal tuo dominio, le tue esigenze aziendali richiedono che tu collabori 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, una per ogni transazione. I profili vengono visualizzati come classi autonome con un nome e Transazioni come classi standalone con una stringa e Cliente con un profilo e una raccolta di transazioni.

Quindi crei un livello ACL che consentirà la traduzione tra il cliente e il cliente dell'altro sistema. In questo modo, non devi mai usare il Cliente dell'altro sistema, devi semplicemente comunicare all'ACL: "Dammi il Cliente con Profilo X, e l'ACL dice all'altro sistema di dargli un Cliente di nome X.name, e restituisce tu un cliente con profilo X.

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

Tutti e tre sono relativamente simili, perché sono tutti modelli indiretti. Ma si rivolgono a diverse strutture, classi / oggetti rispetto alle API rispetto a moduli / sottosistemi. Potresti averli tutti combinati se necessario. Il sottosistema ha un'API complessa, quindi si crea una FACADE per essa, utilizza un modello diverso, quindi per ogni rappresentazione dei dati che non si adatta al modello, si TRANSLATERà di nuovo i dati nel modo in cui li si modella. Infine, forse le interfacce sono anche incompatibili, quindi useresti ADATTATORI per adattarsi da uno all'altro.

    
risposta data 14.11.2014 - 04:08
fonte
11

Molte risposte qui dicono che gli ACL sono "non solo" sul wrapping del codice disordinato. Vorrei andare oltre e dire che non lo sono affatto, e se lo fanno, allora è un vantaggio collaterale.

Un livello anti-corruzione riguarda la mappatura di un dominio su un altro in modo che i servizi che utilizzano il secondo dominio non debbano essere "corrotti" dai concetti del primo. ACL sono per i modelli di dominio quali 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 avvolta come confusa o meno è irrilevante. È quello che è, ho solo bisogno di avere un'interfaccia diversa.

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

    
risposta data 10.06.2016 - 12:09
fonte

Leggi altre domande sui tag