Monday 15 October 2018

Adapter Pattern

The adapter pattern is used to facilitate the use of an object inside of a component even if the component expects an interface that the object does not implement; to facilitate the component's use of the object, we can wrap the object in an adapter that implements the interface the component expects. let's take a look at some UML



In this situation let's say that we have instances of the person class, and we are using a HumanOutputer, now our HumanOutputer accepts objects that realize the IHuman interface which our Person objects are not. so what we do is create a PersonToHumanAdapter in the middle that has a Person and implements the IHuman interface and exposes what we need from the person class through that IHuman implementation.

Let's take a look at the code to get a better understanding of it

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }

    public Person(string firstName, string lastName, DateTime birthDate)
    {
        FirstName = firstName;
        LastName = lastName;
        BirthDate = birthDate;
    }

}

This class simply represents a our person, now let's take a look at our HumanOutputter  class who's job is to take in an enumerable of humans and just print them to the console.

static class HumanOutputter
{
    public static void Write(IEnumerable<IHuman> humans)
    {
        foreach (var h in humans)
            Console.WriteLine($"{h.Name} ({h.Age})");
    }
}

now even without seeing the IHuman interface, it's pretty obvious that our Person model doesn't implement said interface, but we clearly have the information in our Person model that our HumanOutputter needs.

interface IHuman
{
    string Name { get; set; }
    int Age { get; }

}

In comes our PersonToHumanAdapter

class PersonToHumanAdaptorIHuman
{
    Person _person;
    public PersonToHumanAdaptor(Person person) => _person = person;
       
    public string Name {
        get => $"{_person.FirstName} {_person.LastName}";
        set {
            var fullName = value.Split(' ');
            _person.FirstName = fullName[0];
            _person.LastName = fullName[1];
        }
    }

    public int Age {
        get {
            var today = DateTime.Now;
            var age = today.Year - _person.BirthDate.Year;

            return _person.BirthDate > today.AddYears(-age) ? --age : age;
        }          

    }
}

now all our "Adapter" class does is takes in a person as a constructor variable and implements the IHuman interface, then uses the data in the Person field to surface the values that our Human outputter is going to need.

class Program
{
    static void Main(string[] args)
    {
        var People = new Person[] {
            new Person("Pawel", "Chooch", new DateTime(1984,1,31)),
            new Person("Marin", "Smatzki", new DateTime(1983,12,2)),
            new Person("Bob", "Smith", new DateTime(1985,7,21))
        };

        IEnumerable<IHuman> humans = People.Select(p => new PersonToHumanAdaptor(p));
        HumanOutputter.Write(humans);
    }

}

And that's basically the adapter pattern, it wraps objects so that they can be used by other components.

The adapter pattern can also be used when developing a framework to facilitate future compatibility. By leveraging the adapter pattern we facilitate the Open/Close principle of  the SOLID principles; being that our objects should be open to extension, but closed to modification.