Saturday, February 28, 2015

Good practice: Abstract class vs. Interface


Abstract class: Like a Noun! A group of similar things you can group together with one word. Can have implementation of methods and/or declared non-implemented methods. May or may not include abstract methods. However, if you have an abstract method, it does have to be inside of an abstract class (Got it? Just like rectangle is a square, but square is not a rectangle). A non-abstract class derived from an abstract class (abstract class itself cannot be instantiated) must include actual implementations of all inherited abstract methods and accessors.

Abstract Class Example
    abstract class Pokemon
    {  
        // Properties
        protected int myLevel;
        public abstract int Level {get; set;}
        
        // Going to make this an abstract int just to show the difference
        protected int myHP;
        public abstract int HP {get; set;}

        // Can have methods that are DECLARED but NOT IMPLEMENTED
        abstract public int HPCalculator();

        // Can have methods that are IMPLEMENTED. These two are also NON-ABSTRACT
        public int Attack()
        {
            return enemyHP - attackDamage;
        }     
               
        public int Heal(object potion)
        {
            return myHP + potionAmount; 
        }
    }    

    class Pikachu : Pokemon
    {
        public override int Level
        {
            get 
            {
                return myLevel;
            }
            set 
            {
                myLevel = value;
            }
        }

        public override int HP
        {
            get 
            {
                return myHP;
            }
            set 
            {
                myHP = value;
            }
        }

        public override int HPCalculator()
        {
            return Level * 5;
        }
    }

    static void Main(string[] args)
    {
        // You can nickname your Pikachu! Let's call him KetchumChu.
        Pikachu KetchumChu = new Pikachu();
        
        // Set Level property
        KetchumChu.Level = 34;

        // His HP is going to end up being 34*5 = 170 HP.
        KetchumChu.HP = HPCalculator();

        // BATTLE!!! 
        KetchumChu.Attack();
        KetchumChu.Heal(purplePotion);
    }

Interface: Like a Verb (Action)! Things that your class can do. The whole Interface must be implemented in the classes that implement it, you cannot pick an choose pieces you have to take the whole thing (all or nothing!)

Bare minimum blueprint instructions of what has to be inherited at a minimum. NO IMPLEMENTATION. You want a house? You need a floor, 4 walls, and a roof. You do have to implement every single thing mentioned in the interface. So don't put a chimney and fireplace in your interface if you live in Florida.

Interface Example:
interface IAmCuteInterface
{
    // These are DECLARED but NOT IMPLEMENTED in the interface.
    void MakeCuteFace();
}

interface IAmThunderTypeInterface
{
    // These are DECLARED but NOT IMPLEMENTED in the interface.
    void Thundershock();
}

// this new class can implement as many interfaces as it wants!! You HAVE TO implement everything from both interfaces now. 
class Pikachu : IAmCuteInterface, IAmThunderTypeInterface
{
    // Explicit interface member implementation:  
    void IAmCuteInterface.MakeCuteFace()
    {
        // Method implementation.
    }

    void IAmThunderTypeInterface.Thundershock(object potion)
    {
        // Method implementation. Zzzzzap. 
    }

    static void Main()
    {
        // Declare an interface instance.
        Pikachu KetchumChu= new ImplementationClass();

        // Using the thundershock method. 
        KetchumChu.Thundershock();

        // Cheeky ain't he?
        KetchumChu.MakeCuteFace();
    }
}

Don't put in extra stuff that some things will need and other won't, put in things that you know every single thing would need. For a car to work.. you need tires, an engine, and brakes. Don't add an electric motor to your IDriveable, you don't have to have an electric motor. You can specify that later. You know you don't want to ever instantiate a "car" as no one really has just "generic car" they have their Ford Mustang, Tesla, Corvette, whatever.

You can inherit many interfaces! As many as you like! You can make many separate interfaces for different components and inherit what you need out of it. Let's say we have multiple interfaces.. one each for hands, feet, head, ears, fur, tail, scales, wings:

  • Lizard implements hands, feet, head, tail, scales
  • Cat implements hands, feet, head, ears, fur, tail
  • Bird implements feet, tail, and wings
  • Fire breathing dragon implements hands, feet, head, ears, tail, scales, wings
Summary: You use abstract classes and interfaces to add functionality, increase code re-useability/reduce repetitive code, and streamline functionality. Yes you could avoid using these completely, but your code would eventually get very repetitive and you will find yourself doing a lot of extra and unnecessary work. Don't put in extra stuff into either, just the bare minimum that has to fit into whatever inherit/implements the abstract class/interface. If you find yourself wanting to only take pieces of an abstract class or an interface, try breaking them up into difference classes/interfaces, there was probably a better way to design (plan ahead :) ?). Use an abstract class as a base when you are making many similar things that you can group together (i.e. mammals) and use interfaces (as many as you want!) for small functional parts.

Takeaway from examples: Based on my example.. Pokemon makes a great base class. Every type of Pokemon from Pikachu to Charizard will have HP and be able to Attack() and can Heal(). However, NOT every Pokemon is considered cute or a thunder type.. so these make great Interfaces. Some Pokemon are BOTH cute and thunder type like Pikachu and thus can inherit both interfaces. Pikachu can take the one base class Pokemon, plus both interfaces as seen below:
class Pikachu : Pokemon, IAmCuteInterface, IAmThunderTypeInterface