1/25/14

Adapter Design Pattern

The Adapter design pattern is one of the most basic structural design patterns and it's definition is as follows:

"Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces."

Last week I was working on a part of a web application that pulled a list of users from the Database. This application has a pretty good amount of traffic and as the traffic increased it started putting a lot of load on the DB and slowing down the application. In an attempt to find a viable solution, we decided to try out an in-memory cache to store the user data and speed up access. About 25% of the traffic would get the data from the Cache and the rest would continue to go to the Database.

The code changes for the cache repository had to fit seamlessly into the existing design and the changes had to be made without touching the existing implementation for DB calls. The Adapter pattern turned out to be a perfect choice for the requirement.

Following is the code with just the call to the Database:

public class DbRepository
{
  public IEnumerable<user> GetUsers(int userId)
  {
    //call DB and get data
  }
}

//code calling the repository
public class UserService
{
  public IEnumerable<user> GetAllFriendsFor(int loggedInUserId)
  {
    //call DB repository to get data
   DbRepository repository = new DbRepository();
   return repository.GetUsers(loggedInUserId);    
  }
}

Following is the code after implementing the Adapter design pattern:

public interface IRepository
{
  IEnumerable<user> GetUsers(int userId);
}

public class CacheRepository:IRepository
{
  public IEnumerable<user> GetUsers(int userId)
  {
    //call cache and get data
  }
}

//the adapter
public class DbRepositoryAdapter:DbRepository,IRepository
{
}

public class RepositoryFactory
{ 
  //a variable that determines if the user is in test
  bool UserInCacheTest {get;set;}
  
  //return the appropriate repository
  public IRepository GetRepository()
  {
    if(UserInCacheTest)
   return new CacheRepository();
 
 return new DbRepositoryAdapter();
}
}


//code calling the repository
public class UserService
{
  public IEnumerable<user> GetAllFriendsFor(int userId)
  {
    //call the factory to get the appropriate repository
   IRepository repository = new RepositoryFactory().GetRepository();
   return repository.GetUsers(userId);    
  }
}
Explanation:

  • We create a new interface called IRepository that has a single method. This method will have the same signature as the method in the exisiting DbRepository since we are going to be adapting that repository to fit into the new design. NOTE: Using the same signature as in the existing DbRepository is just a matter of convenience, it is not necessary to do so.
  • We then create a new repository called DbRepositoryAdapter that will inherit from the existing DbRepository and implements the IRepository interface.The DbRepository already has a method with the same signature as the method in IRepository, so it is safe to say that the DbRepositoryAdapter implements the IRepository interface. NOTE: If the signature of the interface method was different, we would need to implement that method and call the DbRepository's method from inside it.
  • We create a new class called CacheRepository that also implements IRepository. 
  • We create a factory class called RepositoryFactory that will return a repository of type IRepository depending on whether the user is in the cache test. 
  • The CacheRepository and the DbRepositoryAdapter both implement IRepository, so we can use interface polymorphism within the UserService to return the appropriate repository.
  • We have incorporated the existing DbRepository into our new design by creating an adapter class on top of it. This way the existing DbRepository class and the new CacheRepository class can work well together.(NOTE: we have not touched the existing DbRepository in any way.). This is the advantage of using the Adapter design pattern.

No comments: