Design pattern

Adapter pattern

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

 class MainApp
  {  
    static void Main()
    {   
      Target target = new Adapter();
      target.Request(); 
      Console.ReadKey();
    }
  }

class Target
  {
    public virtual void Request()
    {
      Console.WriteLine("Called Target Request()");
    }
  }

class Adapter : Target
  {
    private Adaptee _adaptee = new Adaptee();

    public override void Request()
    {  
      _adaptee.SpecificRequest();
    }
  }

class Adaptee
  {
    public void SpecificRequest()
    {
      Console.WriteLine("Called SpecificRequest()");
    }
  }



HttpContext cache with adapter pattern



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

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

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

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

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

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

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



No comments:

Post a Comment