Imagine you have to use someone else's code that is designed as shown below:

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) { /* ... */ } }

Now imagine you find out that your code that depends on it looks like the following:

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? }

...and that you want to make it easier to use, in particular, to get rid of repetitive usage of parameters that just aren't needed for your application.

Okay, so you start building an anti-corruption layer.

First thing is to make sure that your "main code" doesn't refer to Messy directly. For example, you arrange dependency management in such a way that trying to access Messy fails to compile. Second, you create a dedicated "layer" module that is the only one accessing Messy and expose it to your "main code" in a way that makes better sense to you.

Layer code would look like the following:

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); } }

As a result, your "main code" does not mess with Messy , using Reasonable instead, about as follows:

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"); }

Note there is still a bit of a mess messing with Messy but this is now hidden reasonably deep inside Reasonable , making your "main code" reasonably clean and free of corruption that would be brought there by direct usage of Messy stuff.

Above example is based on how Anticorruption Layer is explained at c2 wiki:

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.

Note example is intentionally made simple and condensed to keep explanation brief.

If you have a larger mess-of-API to cover behind the anti-corruption layer, same approach applies: first, make sure your "main code" doesn't access corrupted stuff directly and second, expose it in a way that is more convenient in your usage context.

When "scaling" your layer beyond a simplified example above, take into account that making your API convenient is not necessarily a trivial task. Invest an effort to design your layer the right way, verify its intended use with unit tests etc.

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.

For the sake of completeness, notice subtle but important difference between this and related patterns Adapter and Facade. As indicated by its name, anticorruption layer assumes that underlying API has quality issues (is "corrupted") and intends to offer a protection of mentioned issues.

You can think of it this way: if you can justify that library designer would be better off exposing its functionality with Reasonable instead of Messy , this would mean you're working on anticorruption layer, doing their job, fixing their design mistakes.

As opposed to that, Adapter and Facade do not make assumptions on the quality of underlying design. These could be applied to API that is well designed to start with, just adapting it for your specific needs.

Actually, it could even be more productive to assume that patterns like Adapter and Facade expect underlying code to be well designed. You can think of it this way: well designed code shouldn't be too difficult to tweak for particular use case. If it turns out that design of your adapter takes more effort than expected, this could indicate that underlying code is, well, somehow "corrupted". In that case, you can consider splitting the job to separate phases: first, establish an anticorruption layer to present underlying API in a properly structured way and next, design your adapter / facade over that protection layer.