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

139

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

Ho effettuato alcune ricerche, 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. La prima cosa è assicurarsi che il tuo "codice principale" non faccia riferimento a Messy direttamente. Ad esempio, disponi 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");
}

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

L'esempio sopra è basato sul modo in cui 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, lo stesso approccio si applica: in primo luogo, assicurati che il tuo "codice principale" non acceda roba corrotta direttamente e in secondo luogo, esponilo in un modo che è più conveniente nel tuo contesto di utilizzo.

Quando "ridimensiona" il tuo livello oltre un esempio semplificato sopra, considera che rendere conveniente l'API non è necessariamente un compito banale. Investi un sforzo su 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. Puoi pensare in questo modo: il codice ben progettato non dovrebbe essere troppo difficile da modificare per casi d'uso particolari. Se risulta 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
35

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 più alto 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 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 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 tutti i 50 che la libreria può 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, uno per ogni transazione. Puoi visualizzare i profili 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 dire 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 ai moduli / sottosistemi. Potresti averli tutti combinati se necessario. Il sottosistema ha un'API complessa, quindi puoi creare una FACADE per essa, utilizza un modello diverso, quindi per ogni rappresentazione dei dati che non si adatta al tuo modello, dovrai TRANSLATARE quei dati nel modo in cui li modelli. 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, questo è un vantaggio collaterale.

Un livello anti-corruzione riguarda la mappatura di un dominio su un altro in modo che i servizi che usano il secondo dominio non debbano essere "corrotti" dai concetti del primo. ACL sono per i modelli di dominio quali adattatori sono per 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