Friday, 27 October 2017

Bridge Pattern

The bridge pattern is described as "Decoupling an abstraction from it's implementation so that the two can vary independently." When we think of what an abstraction is, we can think of an interface and the implementation would be the coupling of that interface and the class that implements it;

using System;
using System.Linq;

namespace pc.patternBridge
{
    interface IPerson
    {
        string Name { get; set; }
        void Display();
    }

    class Person : IPerson
    {
        public string Name { get; set; }
        public Person(string name) => Name = name;
        public virtual void Display() => Console.WriteLine($"\n{Name}");
    }

    class StudentPerson 
    {
        static int _runningID = 0;
        public int Id { get; set; } = _runningID++;
        public string[] Classes { get; private set; } = new string[6];
        public Student(string name, params string[] classes) : base(name) 
            => Classes = classes;
       
       public override void Display()
        {
            base.Display();
            Console.WriteLine("StudentId: " + Id);
            Console.WriteLine("Classes: " + Classes.Aggregate((x, y) => $"{x}, {y}"));  
        }
    }

    class EmployeePerson 
    {
        static int _runningID = 0;
        public int Id { get; set; } = _runningID++;
        public string JobTitle { get; set; }
        public Employee(string name, string jobTitle) : base(name) 
            => JobTitle = jobTitle;

        public override void Display()
        {
            base.Display();
            Console.WriteLine("Job Title:" + JobTitle);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var people = new Person[] {
                new Person("Pawel Chooch"),new Person("Marin Smartzki"),
                new Student("Magda Tievonik", "Physics", "Math", "Chemestry" ),
                new Student("Jakub Tievonik", "Art", "English Lit", "Poetry" ),
                new Employee("Cezar Zenon", "Software Engineer")
            };

            foreach (var p in people)
                p.Display();
        }
    }
}


now if we look at the above code we see that our display method is defined in our IPerson interface and tightly coupled to our person class, we then override it in our subclasses. now in this instance it's fine, but if for some reason we wanted our code to have some sort of logic that could decide how to print out our various models, or perhaps we wanted the ability to provide each model in our hierarchy a different implementation of our Display function we'd have to separate our display abstraction from our person class, so what we can do is to create an abstract display class.

interface IPerson
{
    string Name { get; set; }
    void Display();
}

abstract class Display
{
    public abstract string Print(object human);
}

class Person : IPerson
{
    Display _display;
    public string Name { get; set; }
    public Display(Display display) => _display = display;
    public Display(Display display, string name)
        : this(display) => Name = name;
    public void Display() => Console.WriteLine(_display?.Print(this));
}

now what we did here is we created an abstract Display class that has one function that returns a string, that is the Print function that takes in an object called human. Then in our Person implementation of the Display method from the IPerson interface, we output the result of our print function from our Display abstraction. 

now since we are now passing in our display behavior to our base person class, we no longer have to override it in our subclasses.

class StudentPerson 
{
    static int _runningID = 0;
    public int Id { get; set; } = _runningID++;
    public string[] Classes { get; private set; } = new string[6];
    public Student(Display display, string name, params string[] classes)
        : base(display, name) => Classes = classes;
}

class EmployeePerson 
{
    static int _runningID = 0;
    public int Id { get; set; } = _runningID++;
    public string JobTitle { get; set; }
    public Employee(Display display, string name, string jobTitle)
        : base(display, name) => JobTitle = jobTitle;
}

So now you can see that our subclasses no longer override the Display method, but instead what we are now going to need is implementations of our Display abstraction.

class PersonDisplay : Display
{
    public override string Print(object human)
    {
        var _person = human as Person;
        return $"Person: {_person.Name}";
    }
}

class StudentDisplay : Display
{
    public override string Print(object human)
    {
        var _student = human as Student;
        var classes = _student.Classes.Aggregate((x, y) => $"{x}, {y}");
        return $"Student #:{_student.Id}; {_student.Name} is taking {classes}";
    }
}

class EmployeeDisplay : Display
{
    public override string Print(object human)
    {
        var _employee = human as Employee;
        return $"{_employee.Name} works as a {_employee.JobTitle}";
    }
}

what we did here is took that human parameter of type object and cast it as our relative objects, we then created a custom return string based on each type. 

class Program
{
    static void Main(string[] args)
    {
        var pd = new PersonDisplay();
        var sd = new StudentDisplay();
        var ed = new EmployeeDisplay();

        var people = new Person[] {
            new Person(pd, "Pawel Chooch"), new Person(pd, "Marin Smartzki"),
            new Student(sd, "Magda Tivonik", "Physics", "Math", "Chemestry" ),
            new Student(sd,"Jakub Tivonik", "Art", "English Lit", "Poetry" ),
            new Employee(ed, "Cezar Zenon", "Software Engineer")
        };

        foreach (var p in people)
            p.Display();
    }
}

in our main we then instantiate our three refined abstractions that inherit from our display abstraction and we pass them into our concrete classes.

using System;
using System.Linq;

namespace pc.patternBridge
{
    interface IPerson
    {
        string Name { get; set; }
        void Display();
    }

    abstract class Display
    {
        public abstract string Print(object human);
    }

    class Person : IPerson
    {
        Display _display;
        public string Name { get; set; }
        public Person(Display display) => _display = display;
        public Person(Display display, string name)
            : this(display) => Name = name;
        public void Display() => Console.WriteLine(_display?.Print(this));
    }

    class Student : Person
    {
        static int _runningID = 0;
        public int Id { get; set; } = _runningID++;
        public string[] Classes { get; private set; } = new string[6];
        public Student(Display display, string name, params string[] classes)
            : base(display, name) => Classes = classes;
    }

    class Employee : Person
    {
        static int _runningID = 0;
        public int Id { get; set; } = _runningID++;
        public string JobTitle { get; set; }
        public Employee(Display display, string name, string jobTitle)
            : base(display, name) => JobTitle = jobTitle;
    }

    class PersonDisplay : Display
    {
        public override string Print(object human)
        {
            var _person = human as Person;
            return $"Person: {_person.Name}";
        }
    }

    class StudentDisplay : Display
    {
        public override string Print(object human)
        {
            var _student = human as Student;
            var classes = _student.Classes.Aggregate((x, y) => $"{x}, {y}");
            return $"Student #:{_student.Id}; {_student.Name} is taking {classes}";
        }
    }

    class EmployeeDisplay : Display
    {
        public override string Print(object human)
        {
            var _employee = human as Employee;
            return $"{_employee.Name} works as a {_employee.JobTitle}";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var pd = new PersonDisplay();
            var sd = new StudentDisplay();
            var ed = new EmployeeDisplay();

            var people = new Person[] {
                new Person(pd, "Pawel Chooch"), new Person(pd, "Marin Smartzki"),
                new Student(sd, "Magda Tivonik", "Physics", "Math", "Chemestry" ),
                new Student(sd,"Jakub Tivonik", "Art", "English Lit", "Poetry" ),
                new Employee(ed, "Cezar Zenon", "Software Engineer")
            };

            foreach (var p in people)
                p.Display();
        }
    }
}

now this has mostly been an exercise in exploring this pattern, one that seems very vague, confusing and poorly explained also i've never really seen or heard of it in use, however it does remind me of Inversion of control, which would explain why no one refers to this pattern. so for me i'm going to think of the bridge patter of basically IOC but with abstract classes instead of interfaces.