Tuesday, August 7, 2012

One Reason to Change

The current book in the reading assignments is, Agile Software Development: Principles, Patterns, and Practices; by Robert C. Martin. Although I will end up doing a general book review post for this when I'm done, I feel the content of the book is important enough for me to post about it as I go.

So, this post is about, The Single-Responsibility Principle (SRP).

What is this Single-Responsibility thingy?

"A class should have only one reason to change."

The idea behind SRP is that your classes should only be responsible for one aspect of the design, so that when you have to change the code, you are not forced to go back into a file for more than one reason. 

Say you are passing this around your system:

    interface ICharacter
    {
        Attack();
        Defend();
        EquipItem(Guid itemId, string location);
        GainExperience(int xpValue);
        ...

So this is only part of what it would take to have some character code, we could add a bunch to the idea, but its enough to get the idea. Off the bat, passing this around, you can see that anyone with an ICharacter can call anything on it, even if it has no business doing so. But using those examples there, you can pretty easily see 3 basic reasons it could change; combat actions (attack & defend), managing equipment (equip item) and leveling up (gain experience). As it stands not only would the system itself be vulnerable to changing for a variety of reasons anywhere ICharacter was passed, but ICharacter itself would probably change during any meaningful change to the system.

Say you wanted to change how attacks happen, or gaining experience shouldn't be based on a actual value but rather some kind of monster difficulty, or you want to be able to equip an item that takes up more than one location. All of these separate reasons to change lead you back to the single place of ICharacter.

And that matters why?

The generic reason here is that, like the other principles, you do this to help decouple the parts of your code from each other. So you can build out new functionality or change existing parts and have as little places as possible to touch. The less places you need to touch the less places you need to investigate to understand what is going on in your system. And of course, the less chances of causing side effects.

Another aspect to think about is the dependencies of each of these responsibilities. When a class has more than one responsibility you also need the dependencies for each. So if you have a dependency of a IEquipmentContainer for your EquipItem call, anywhere where you want to call Attack would also need it, as the system only knows about ICharacter.

So what could it look like after SRP?

For our fake class, I would go in this direction to utilize SRP:

    interface ICombatActions
    {
        Attack();
        Defend();
    }

    interface IEquipmentManagement
    {
        EquipItem(Guid itemId, string location);
    }

    interface ILevelCapable
    {
        GainExperience(int xpValue);
    }

Depending on what it means to attack & defend, you could further break those up. However its enough for SRP to just assume right now that they are super simple and just opposites of each other. At any rate, now that they have been split up by their responsibilities you pass the individual parts around your system and decouple the rest.

    interface ICombatActions
    {
        Attack(Monster target);
        Defend();
    }

Now if they come back to you to tell you there can be more than one bad guy you are fighting at a time, you only need to change Attack to take in a target. And per the dependencies issue, if you need a backback to equip an item from, the parts of the system that do the attack/defend combat calls don't care.