Understanding Interfaces

My first contact with interfaces was so bad that I forgot they even existed. This short tutorial aims to take a look at this concept demonstrate with an example.

Project can be downloaded here.

Some code might be slightly different from the project code due to subtle changes.

Maybe you have heard before “An Interface is a contract of functionality”, but what does that mean?

In C# there is not multiple inheritance, the solution for this problem is a layer of abstraction called an Interface. When we implement an interface in a class we are saying that the class will behave in a certain way.

Classes implementing interfaces allow us to call methods even if their inheritance tree is not directly connected.

Imagine a SpaceCar with an interface IFlyable which has the method Fly().

But now we have a Bird, and a he is IFlyable too, but a Bird is an Animal, on the other hand im pretty sure a SpaceCar isn’t.

The good thing is that we dont need to know what is what, Bird and SpaceCar both implement their Fly() method separately, maybe the car consumes 2x+ fuel when flying, maybe the bird stops singing when he flies.

If we have a list of IFlyable we just need to call Fly() and there it goes.

The example:

(For the sake of the tutorial lets assume a WaterPistol is a Gun and a Slingshot is not)

structure

The interface IShootable has 1 method that outputs damage to a target with 2 parameters, one for the target and an optional parameter attackModifier which is going to affect the damage.

Methods in interfaces are just declared but not implemented, this means they have no code whatsoever.

public interface IShootable
{
  string ShootAtTarget(ITargetable target, AttackModifierType attackModifier = AttackModifierType.None);
}

The interface ITargetable has a method that takes care of the damage with 3 parameters, the damageSource, the damageAmmount and an optional parameter for attackModifier.

public interface ITargetable
    {
        string TakeDamage(string damageSource, int damageAmmount, AttackModifierType attackModifier = AttackModifierType.None);
    }

Now that the interfaces are set, lets start with the Weapon class.

So the weapon is an abstract class, we wont instantiate it, it can be a melee weapon…a ranged wapon… etc.

(Note: Some properties may serve as demonstration only)

public abstract class Weapon
    {
        // Some properties may serve as demonstration only.

        public int Durability { get; set; }
        public int Damage { get; set; }
        public AttackModifierType AttackModifier { get; set; }
    }

The Gun class inherits from Weapon and it is too an abstract class.

public abstract class Gun : Weapon
 {
 // Some properties may serve as demonstration only.

 public int ShootSpeed { get; set; }
 public int Ammo { get; set; }
 public int Recoil { get; set; }
 }

So this far we have 2 Interfaces, IShootale and ITargetable.

We have 2 abstract classes, Weapon and Gun which inherits from the previous.

Lets build our weapons already!

Starting with a bazooka, it inherits from Gun and implements IShootable.

Implementing an Interface is pretty easy:

  1. Follow a Public class declaration (Bazooka) with a colon, then the abstract class (Gun) and the Interface (IShootable) separated by a comma.
  2. In your class declare all the interface methods as public.

So a bazooka shoots differently from a WaterPistol, maybe the bazooka gives the user some damage…. the ShootAtTarget(…) implementation is different in these two cases.

(The bazooca constructor sets the ShootSpeed and Damage hardcoded just for demo purposes )

 public class Bazooka : Gun, IShootable
 {
  public Bazooka()
  {
   ShootSpeed = 50;
   Damage = 100;
  }

  public string ShootAtTarget(ITargetable dummy, AttackModifierType attackModifier = AttackModifierType.None)
  {
    // Affect other properties here

    return dummy.TakeDamage(
    damageSource: 'Bazooka';,
    damageAmmount: Damage,
    attackModifier: AttackModifier);
  }
 }

Same old same old for the WaterPistol class.

 public class WaterPistol : Gun, IShootable
    {
        public WaterPistol()
        {
            ShootSpeed = 1;
            Damage = 1;
        }       

        public string ShootAtTarget(ITargetable dummy, AttackModifierType attackModifier = AttackModifierType.None)
        {
            // Affect other properties here

            return dummy.TakeDamage(
                damageSource: 'water pistol',
                damageAmmount: Damage,
                attackModifier: AttackModifier);
        }
    }

And the SlingShot

 public class SlingShot : Weapon, IShootable
    {
        public SlingShot()
        {
            Damage = 10;
        }

        public string ShootAtTarget(ITargetable dummy, AttackModifierType attackModifier = AttackModifierType.None)
        {
            // Affect other properties here            

            return dummy.TakeDamage(
                damageSource: 'slingshot',
                damageAmmount: Damage,
                attackModifier: AttackModifier);
        }
    }

Now the Target, lets create a PracticeDummy .

(Explanation below)

 public class PracticeDummy : ITargetable
    {
        // Number of ticks when taking damage
        int numberOfDamageTicksOverTime = 5;
        public PracticeDummy(int health)
        {
            Health = health;
        }

        public int Health { get; set; }

        public string TakeDamage(string damageSource, int damageAmmount, AttackModifierType attackModifier = AttackModifierType.None)
        {

            Health -= damageAmmount;

            Console.WriteLine(string.Format('ouch i took {0} damage from a {1}', damageAmmount, damageSource));
            Console.WriteLine(string.Format('now i have {0} health', Health));

            HandleDamageModifier(attackModifier);           

            if (Health == 0)
            {
                return ' XXX bye bye dummy XXX';
            }
            else
            {
                return 'dummy survived';
            }
        }

        void HandleDamageModifier(AttackModifierType attackModifier = AttackModifierType.None)
        {
            switch (attackModifier)
            {
                case AttackModifierType.None:
                    break;

                case AttackModifierType.Fire:
                    HandleFireModifier();
                    break;

                case AttackModifierType.Water:
                    Console.WriteLine('Water modifier not implemented');
                    break;

                case AttackModifierType.DuplicateDamage:
                    Console.WriteLine(';Water modifier not implemented');
                    break;

                default:
                    Console.WriteLine('error: invalid attack modifier');
                    break;
            }
        }

        void HandleFireModifier()
        {
            int fireDamage = 3;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine('FIRE ATTACK MODIFIER !! (burning)');

            Console.ResetColor();
            for (int tick = 0; tick < numberOfDamageTicksOverTime; tick++)
            {

                Console.WriteLine(string.Format(
                                    'burning inflicts {0} damage. Tick {1}/{2}',
                                    fireDamage,
                                    tick +1,
                                    numberOfDamageTicksOverTime));

                Health -= fireDamage;
            }
            Console.WriteLine(string.Format('now i have {0} health';, Health));
        }
    }

A practice dummy will TakeDamage() and modify its properties, the HandleDamageModifier() will see which type of modifier was added to the attack and act accordingly, for example  fire will burn the dummy for 3 damage over 5(numberOfDamageTicksOverTime) ticks.

As for the main Program.

 public enum AttackModifierType
    {
        None,
        Fire,
        Water,
        DuplicateDamage
    }

    class Program
    {
        static void Main(string[] args)
        {

            // create my shootables
            var b1 = new Bazooka();

            var w1 = new WaterPistol();

            var s1 = new SlingShot();
            // set the attackmodifier of the sligshot 1 to fire.
            s1.AttackModifier = AttackModifierType.Fire;

             IEnumerable shootableCollection = new List<IShootable> { b1, w1, s1 };

            foreach (var shootable in shootableCollection )
            {
                // Create a new dummy for every gun
                // otherwise we would be shooting the same dummy with all those guns....poor dummy
                PracticeDummy dummy = new PracticeDummy(health: 100);

                Console.WriteLine(shootable.ShootAtTarget(dummy));

                Console.WriteLine('-------------------------------------------------');

            }
                Console.ReadLine();
        }
    }

We declare our enum AttackModifier.

And in Main, we create our “IShootables”and add a Fire AttackModifier to the slingshot.

Then we create an IEnumerable of type IShootalble passing the weapons we just created.

And at last, for each IShootable in the list, we create a new PracticeDummy and shoot him with whatever weapon/shootable is in that list.

Console output:

console-output

And thats it, hope you found this helpfull in some way.

Thanks for reading!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s