Thursday, 9 July 2015

Inheritance

I've hinted at this topic a number of times already, but I want to discuss it explicitly. Inheritance, the foundation of Object Oriented programming or OOP for short.

You can think of OOP as a hierarchy, for simplicity, we'll explore the relationship between Person and Employee. A Person can be an Employee whereas an Employee must be a Person, meaning that in our hierarchy "Person" is an ancestor of "Employee" otherwise known as a super class. Because an Employee will have all of the traits of a Person, however the same is not true the other way around, this is why we can use Person as a base for employee.

Person Employee
  • First Name
  • Last Name
  • Birth date
  • First Name
  • Last Name
  • Birth date
  • Employee Id

Above we can clearly see that our Employee and Person representations share a number of characteristics, thus rather than creating two separate representations of each, we can use inheritance to share the characteristics of person to employee, so that after we create our person class, for our Employee class we can inherit person and then just code up the things in employee that do not exist in person

Lets create a simple person class, that could look something like the following


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

    public string FullName { get { return $"{FirstName} {LastName}"; } }

    public Person() { }

    public Person(string FirstName, string LastName) : this()
    {
        this.FirstName = FirstName;
        this.LastName = LastName;
    }

    public string WritePerson()
    {
        return $"{FullName}";
    }
}


In the above, take note of the constructors, in this case we have two, a parameterless constructor which you almost always want to have for the sake of serialization, and one that let's you initiate your class with a person's first and last names. Notice that the constructor with parameters calls the parameterless one with this(). Doing this is a best practice since even though the parameterless constructor in our case does nothing, however in certain circumstances it could potentially have some sort of configuration code. 

Now lets add a third constructor to our Person class and demonstrate leveraging constructor chaining.


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

    public string FullName { get { return $"{FirstName} {LastName}"; } }

    public Person() { }

    public Person(string FirstName, string LastName) : this()
    {
        this.FirstName = FirstName;
        this.LastName = LastName;
    }
   
    public Person(string FirstName, string LastName, DateTime Birthdate)
        : this(FirstName, LastName) {
        this.Birthdate = Birthdate;
    }

    public string WritePerson()=>  $"{FullName}";
}


In the above, notice that our newly added constructor doesn't instantiate our first and last name properties, it only instantiates the birthdate property and leaves the first and last name properties to the first and last name constructor.
 
Next lets create an employee class, keeping in mind that everything in your Person class applies to your employee class. It seems like a waste to copy and past this code into an employee class, and check it out you have 3 properties, 1 readonly property and three constructors that perfectly call each other using the :this(<paramters>) syntax, we went to all this effort not to repeat ourselves. 


class Employee : Person {
    static int runningId = 0;
    public int Id { get; private set; } = runningId++;

    public Employee() : base() {}
    public Employee(string FirstName, string LastName) : base(FirstName, LastName) { }
    public Employee(string FirstName, string LastName, DateTime Birthdate)
        : base(FirstName, LastName, Birthdate) { }

    public string WriteEmployee() => $"{Id}: {base.WritePerson()}";
}


notice that we don't initiate the Id property in our constructor, this is to facilitated serialization. By inheriting from our Person class in our Employee class now inherits everything from Person and has access to everything that is marked as public or internal.


class Program
{
    static void Main(string[] args)
    {
        var e = new Employee("Pawel", "Ciucias");
        Console.WriteLine(e.WriteEmployee());
        Console.WriteLine(e.WritePerson());

        var p = e as Person;
        Console.WriteLine(p.WritePerson());
    }
}


so much so that if we cast our employee instance to a person it'll act as a person.

now you may notice something, not bizarre, but a tad well odd, and that's access to the WritePerson function from an employee, that's because when you inherit from a base class you inherit everything there's no picking and choosing which just means that you have to plan a bit. what may have been a better approach would have been to rename the function to something like WriteValue() and replace it in the employee class.


namespace pc.Inheritance
{
    class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime Birthdate { get; set; }

        public string FullName { get { return $"{FirstName} {LastName}"; } }

        public Person() { }

        public Person(string FirstName, string LastName) : this()
        {
            this.FirstName = FirstName;
            this.LastName = LastName;
        }

        public Person(string FirstName, string LastName, DateTime Birthdate)
            : this(FirstName, LastName)
        {
            this.Birthdate = Birthdate;
        }

        public string WriteValue()
        {
            return $"{FullName}";
        }
    }

    class Employee : Person
    {
        static int runningId = 0;
        public int Id { get; private set; } = runningId++;

        public Employee() : base() { }

        public Employee(string FirstName, string LastName)
            : base(FirstName, LastName) { }

        public Employee(string FirstName, string LastName, DateTime Birthdate)
            : base(FirstName, LastName, Birthdate) { }

        public new string WriteValue()
        {
            return $"{Id}: {base.WriteValue()}";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var e = new Employee("Pawel", "Ciucias");
            Console.WriteLine(e.WriteValue());

            var p = e as Person;
            Console.WriteLine(p.WriteValue());
        }
    }
}


Now in the above we call a different instance of the "WriteValue()" function if we are calling it on a Person class or an Employee class.