Wednesday, August 8, 2012

We don't live in a barn!

Next principle up; The Open-Closed Principle. Like my grandmother used to say; Open for extension, but closed for modification! Wait... she didn't say that, I think it was more like close that door, you don't live in a barn. But it was close enough. We liked to keep the doors open so we could extend where we played, and she liked it closed so we would be outside and not modifying her clean house into a messy one. Yah, I know, I'm being not funny.

So OCP actually states:

    "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification."

What?!

Well "wtf?!", was probably more like my initial response to this one. And some time after that also. How do you implement something if you aren't allowed to modify your code? Well, the answer is to extend your code base for the new behavior. One of the keys here, is to take it as a rule that you can't modify your code. In fact you sometimes have to; a bug, a change in the understanding of existing behaviors, and such would cause modifications. What this principle is helping out with, is when you want to grow your system, ideally you don't want to change your existing code to accomplish it.

Let's open-close that door

So how do you implement new behavior in your system through extension? Primarily through abstraction. Lets look at an example:

Say we have a knight in shining armor riding their magnificent horse (horsey), as our games main character.

    class Hero
    {
        void Attack(Monster target)
        {
            // hack & slash
        }

        void Defend()
        {
            // shield block
        }

        void Move()
        {
            // horsey charge
        }
    }

The hero hacks & slashes the mosters, deflects anything with his shield, and rides horsey around. All is good. Until those player polls come back and all of a sudden people want to play some kind of silly stick waving wizard. Great, now we need to go hack up the hero class. Attack now needs to know to Hack & Slash for the uber-cool knight, and wave some twinkling wand around for the lame wizard.

Ugh, different heros do different things...

    class Hero
    {
        void Attack(Monster target)
        {
            // knights go hack & slash
            // wizards go pew-pew
        }

Right, so we're modifying our code base to account for new behaviors/features. But why is that bad? Because you know, before it's over you'll have code in there for; Knights, Wizards, Rogues, Clerics, Rangers, Blacksmiths, Barbarians, Druids, Fighters, Monks, Sorcerers, Paladins, Assassins, Merchants, and... oh yah, Bards.

So, lest pull the idea of a hero out into an abstraction and let the logic of the individual type of hero be defined in its implementations:

    interface IHero
    {
        void Attack(Monster target);
        void Defend();
        void Move();
    }

So now you've created your contract of what a hero is, and you use this through out your system. Once you have your system setup to use IHero you can now create any number of hero types by only creating new implementations. You aren't modifying any existing Knight code when building out the Wizard. It's open to extension, but closed to modification.

That's how cool knights are.

And then there were two

    class Knight : IHero
    {
        void Attack(Monster target)
        {
            // hack & slash
        }

        void Defend()
        {
            // shield block
        }

        void Move()
        {
            // horsey charge
        }
    }

    class Wizard : IHero
    {
        void Attack(Monster target)
        {
            // twinkly wand waving
        }

        void Defend()
        {
            // roll over and play dead
        }

        void Move()
        {
            // teleport
        }
    }

But wait, there's more!

Yesterday I spoke of The Single-Responsibility Principle, putting it out there my understanding of what it is and some possible ways to utilize it. It turns out that some of the others had done the same, I guess it was SRP blog night last night for some at 8th Light. :)

They aren't kidding when they say that it's one of the easiest principles to learn and one of the hardest to get right. One of the core ideas I've been getting wrong is that you method or class can actually have more than one responsibility and still protect itself from the issues that SRP solves. The key here is cohesion of your code. The idea that if your responsibilities have strong cohesion, that they would change together, and the actual responsibility becomes what those responsibilities represent.

Another interesting aspect of the SRP principle is the number of times I've read it. How I've come to understand it. And with a some dialog with the others, some learning it, others explaining it; how I have changed my view of it, and can see an area to focus my practices on to gain even better understanding.