Design patterns and practices in .NET: the Adapter Pattern

Introduction

The adapter pattern is definitely one of the most utilised designed patterns in software development. It can be used in the following situation:

Say you have a class – Class A – with a dependency on some interface type. On the other hand you have another class – Class B – that would be ideal to inject into Class A as the dependency, but Class B does not implement the necessary interface.

Another situation is when you want to factor out some tightly coupled dependency. A common scenario would be a direct usage of some .NET class, say HttpRuntime in a method that you want to test. You must have a valid HttpRuntime while running the test otherwise the test may fail and that is of course not acceptable. The solution is to let the method be dependent on some abstraction that is injected to it – the question is how to extract HttpRuntime and make it implement some interface instead? After all, it’s unlikely that you’ll have write access to the HttpRuntime class, right?

The solution is to write an adapter that sits between Class A and Class B that wraps Class B’s functionality. An example from the real world: if you travel from Britain to Sweden and try to plug in your PC in your hotel room you’ll fail as the electric sockets in Sweden are different from those in the UK. Class A is your PC’s power plug and Class B is the socket in the wall. The two objects clearly don’t fit but there’s a solution: you can insert a specially made adapter into the socket which makes the “conversion” between the plug and the socket enabling you to pair them up. In summary: the adapter pattern allows classes to work together that couldn’t otherwise due to incompatible interfaces. The adapter will take the form of an interface which has the additional benefit of extensibility: you can write many implementations of this adapter interface making sure your method is not tightly coupled to one single concrete implementation.

Demo

Open Visual Studio and create a new blank solution. Add a new class library called Domain and delete Class1.cs. Add a new class to this project called Customer. We’ll leave it void of any properties, that is not the point here. We want to expose the repository operations by an interface, so insert an interface called ICustomerRepository to the Domain layer:

public interface ICustomerRepository
	{
		IList<Customer> GetCustomers();
	}

Add another class library called Repository and a class called CustomerRepository which implements ICustomerRepository:

public class CustomerRepository : ICustomerRepository
	{
		public IList<Customer> GetCustomers()
		{
			//simulate database operation
			return new List<Customer>();
		}
	}

Add a new class library project called Service and a class called CustomerService to it:

public class CustomerService
	{
		private readonly ICustomerRepository _customerRepository;

		public CustomerService(ICustomerRepository customerRepository)
		{
			_customerRepository = customerRepository;
		}

		public IList<Customer> GetAllCustomers()
		{
			IList<Customer> customers;
			string storageKey = "GetAllCustomers";
			customers = (List<Customer>)HttpContext.Current.Cache.Get(storageKey);
			if (customers == null)
			{
				customers = _customerRepository.GetCustomers();
				HttpContext.Current.Cache.Insert(storageKey, customers);
			}

			return customers;
		}
	}

This bit of code should be straightforward: we want to return a list of customers using the abstract dependency ICustomerRepository. We check if the list is available in the HttpContext cache. If that’s the case then convert the cached value and return the customers. Otherwise fetch the list from the repository and put the value to the cache.

So what’s wrong with the GetAllCustomers method?

Testability

The method is difficult to test because of the dependency on the HttpContext class. If you want to get any reliable result from the test that tests the behaviour of this method you’ll need to somehow provide a valid HttpContext object. Otherwise if the test fails, then why did it fail? Was it a genuine failure, meaning that the customer list was not retrieved? Or was it because there was no HttpContext available? It’s the wrong approach making the test outcome dependent on such a volatile object.

Flexibility

With this implementation we’re stuck with the HttpContext as our caching solution. What if we want to change over to a different one, such as Memcached or System.Runtime? In that case we’d need to go in and manually replace the HttpContext solution to a new one. Even worse, let’s say all your service classes use HttpContext for caching and you want to make the transition to another caching solution for all of them. You probably see how tedious, time consuming and error-prone this could be.

On a different note: the method also violates the Single Responsibility Principle as it performs caching in its body. Strictly speaking it should not be doing this as it then introduces a hidden side effect. The solution to that problem is provided by the Decorator pattern, which we’ll look at in the next blog post.

Solution

It’s clear that we have to factor out the HttpContext.Current.Cache object and let the consumer of CustomerService inject it instead – a simple design principle known as Dependency Injection. As usual, the most optimal option is to write an abstraction that encapsulates the functions of a cache solution. Insert the following interface to the Service layer:

public interface ICacheStorage
	{
		void Remove(string key);
		void Store(string key, object data);
		T Retrieve<T>(string key);
	}

I believe this is quite straightforward. Next we want to update CustomerService to depend on this interface:

public class CustomerService
	{
		private readonly ICustomerRepository _customerRepository;
		private readonly ICacheStorage _cacheStorage;

		public CustomerService(ICustomerRepository customerRepository, ICacheStorage cacheStorage)
		{
			_customerRepository = customerRepository;
			_cacheStorage = cacheStorage;
		}

		public IList<Customer> GetAllCustomers()
		{
			IList<Customer> customers;
			string storageKey = "GetAllCustomers";
			customers = _cacheStorage.Retrieve<List<Customer>>(storageKey);
			if (customers == null)
			{
				customers = _customerRepository.GetCustomers();
				_cacheStorage.Store(storageKey, customers);
			}

			return customers;
		}
	}

We’ve got rid of the HttpContext object, so the next task is to inject it somehow using the ICacheStorage interface. This is the essence of the Adapter pattern: write an adapter class that will resolve the incompatibility of our home-made ICacheStorage interface and HttpContext.Current.Cache. The solution is really simple. Add a new class to the Service layer called HttpContextCacheStorage:

public class HttpContextCacheStorage : ICacheStorage
	{

		public void Remove(string key)
		{
			HttpContext.Current.Cache.Remove(key);
		}

		public void Store(string key, object data)
		{
			HttpContext.Current.Cache.Insert(key, data);
		}

		public T Retrieve<T>(string key)
		{
			T itemsStored = (T)HttpContext.Current.Cache.Get(key);
			if (itemsStored == null)
			{
				itemsStored = default(T);
			}
			return itemsStored;
		}
	}

This is the adapter class that encapsulates the HttpContext caching object. You can now inject this concrete class into CustomerService. In the future if you want to make use of a different caching solution then all you need to do is to write another adapter for that: MemCached, Velocity, System.Runtime.Cache, you name it.

View the list of posts on Architecture and Patterns here.

Advertisements

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

17 Responses to Design patterns and practices in .NET: the Adapter Pattern

  1. udayanga says:

    smell like strategy pattern.

    • Andras Nemes says:

      You sure? I think it’s best if you check out the strategy pattern here.

      • udayanga says:

        I mean, you can implement ICacheStorage interface when you have multiple caching strategies.Then you can call Retrieve,Remove or Store method based on your strategy.correct me if i’m wrong.

      • Andras Nemes says:

        If you have multiple caching concrete caching storage mechanisms that implement ICacheStorage, then yes, you can apply the Strategy pattern to choose the correct one based on some external parameters. However, hiding the concrete implementations behind abstractions is still best done with the Adapter pattern I think.

  2. I think it’s actually Strategy. Adapter makes possible that objects with incompatible interfaces can work together. In this case that is not the problem.

    The problem here is that you need to decouple your CustomerService from the Caching mechanism for testability purposes. That is served with a strategy pattern.

    However I can see that in the future, when you decide to substitute your caching layer for something more advanced like Memcached, it’s possible that you could end with some Caching object that doesn’t conform to your interface. In that case, you’d be actually writing an adapter so your CustomerService can work with it.

    Those were my two cents.

  3. Satish says:

    While this is a good example to explain DI, the need for the adapter pattern doesn’t come until the end. I think the classic example would be when you have a common interface to access say a third party API and you create an adapter for each third party API.

  4. Murali says:

    Andras, I guess you missed out the Customer class. Please add. Thanks.

    • Andras Nemes says:

      Hi Murali,

      It’s an empty class with no properties:

      “Add a new class to this project called Customer. We’ll leave it void of any properties, that is not the point here.”

      //Andras

  5. Pingback: Architecture and patterns | Michael's Excerpts

  6. Steven says:

    Great explanation. Thanks Andras!

  7. Aravind Chembeti says:

    While the post is simple and elegant, adapter pattern doesn’t come very clearly unless one focuses on ‘testability’ nature. For me the classic adapter example in .Net is interop assemblies: you need to deal with COM components from managed (or vice versa) and you hit the runtime constraints which are solved by interop tools.

  8. Ahmed says:

    Thanks for posting,Nice Explanation with good example, Keep it up 🙂

  9. Vlad Vizitiu says:

    Hey Andras,

    I like your explanation though I do have one suggestion.
    When I started learning .NET, the term that kept popping up was “Wrapper”. It might help to explain that the 2 are actually synonymous because I only got to know it as an “Adapter” way later in personal research.

    Wikipedia calls it “Adapter or Wrapper or Translator”.

    Seeing how naming conventions and vocabulary are really important and people across the globe might use different names for this pattern (unofficially, they might not even know it’s a pattern like it was for me)

    Just a suggestion,
    Thank you,
    Vlad V.

  10. Mario says:

    I know its an old post, but .. what happens when you want to test the Adapter class “HttpContextCacheStorage”. Don’t you still have the HttpContext object to deal with ? Its like the problem is being moved from one class into another.

  11. Pingback: Adapter Design Pattern |

  12. Pingback: Arquitetura em C#: o padrão adaptadores – Tiago Pariz

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

Bite-size insight on Cyber Security for the not too technical.

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: