Jay Myers, Software Engineer
March 12, 2018

Writing software can be a lot like being an adventurous hero – or at least that’s what this author tells himself.  Luckily for us, one such similarity is highlighted in this post discussing the Open / Closed Principle, which states that software should be open for extension while remaining closed for modification.  In other words, you shouldn’t have to change things you’ve already written to add or modify behavior in the future.


APISitting on a table in my apartment is quite a large pile of adventure board games. These are board games that aren’t so much like Monopoly, but a lot more like D&D – full of complex world building and mechanics. Board games like Scythe, Arkham Horror, and Gloomhaven. These games are incredibly fun to play, but sometimes the rules and mechanics at play can become a little confusing. Luckily, if a rule is worded strangely or you have a question about a specific situation you can pretty easily check some online forums to see what others have done in that situation. Occasionally you’ll be lucky and the creator of the game will chime in and clarify. Or, if you’re a certain type of person, you can just ignore a rule or two if they’re inconvenient for you.

It’s a little bit harder to deal with complicated and changing rules when you’re talking about code though. If you’re someone who writes software, or even thinks about software design, then this is not news to you. I’m going to describe a guiding principle that’s pretty important in helping you manage changes to rules in any system you’re creating. Important enough to earn it a letter in the SOLID acronym. It’s the Open/Closed Principle.

Simply put, it just means that it would be a good idea to design things in such a way that if you wanted to modify behavior, you could do that without having to reach into an object and change its code directly. That’s a pretty good idea, but how do you actually do something like that? Well, there’s a lot of different ways to achieve this but I think it’s best served with an example. So let’s invent a problem for ourselves, and then solve it.

The setup for this situation is pretty simple – we want to write code that will represent a hero who is on some kind of quest. The only thing we’ll focus on for this example is the way in which our hero will take damage in battle.

Our hero will have some number of hit points, and if they take sufficient damage (i.e. if their current hit points fall to zero) then they, sadly, die.

public class Hero
{
public int CurrentHitPoints { get; set; }public void TakeDamage(int amount)
{
CurrentHitPoints -= amount;
if (CurrentHitPoints <= 0)
Die();
}
}

So far, so good. But let’s make things a little more interesting. Let’s introduce different types of damage. The universe this journey takes place in will have traps and monsters that are capable of dealing normal physical damage as well as shock, frost, and fire damage.

public enum DamageType
{
Physical,
Shock,
Frost,
Fire
}

Luckily, our hero has innate resistances to each of these different types of damage. Those resistances are going to be defined as a percentage by which damage of that type should be reduced. So if we introduce this concept into our existing hero, maybe it looks something like this.

public class Hero
{
public int CurrentHitPoints { get; set; }public decimal PhysicalResist { get; set; }
public decimal ShockResist { get; set; }
public decimal FrostResist { get; set; }
public decimal FireResist { get; set; }public void TakeDamage(int amount, DamageType type)
{
var damageAmount = amount;
switch (type)
{
case DamageType.Physical:
damageAmount -= (int)(amount * PhysicalResist);
break;
case DamageType.Shock:
damageAmount -= (int)(amount * ShockResist);
break;
case DamageType.Frost:
damageAmount -= (int)(amount * FrostResist);
break;
case DamageType.Fire:
damageAmount -= (int)(amount * FireResist);
break;
}CurrentHitPoints -= damageAmount;
if (CurrentHitPoints <= 0)
Die();
}
}

Ok, so… that’s not so bad. It’s not great, but it’s not terrible either. The main problem we are facing right now (other than the fact that our hero cannot heal themselves) is that switch statement. It might look harmless, but think about what might happen if we need to add to it.

Let’s say that we encounter a new kind of trap that will deal both frost and fire damage – and we should only use the lower of those two resistances to calculate the damage. You would need to add a new case to that switch statement, and introduce a new damage type (even though it’s not really a new kind of damage). So our switch statement would end up looking like this:

switch (type)
{
case DamageType.Physical:
damageAmount -= (int)(amount * PhysicalResist);
break;
case DamageType.Shock:
damageAmount -= (int)(amount * ShockResist);
break;
case DamageType.Frost:
damageAmount -= (int)(amount * FrostResist);
break;
case DamageType.Fire:
damageAmount -= (int)(amount * FireResist);
break;
case DamageType.FireAndFrostButYouTakeTheLowestResistance:
damageAmount -= (int)(amount * Math.Min(FrostResist, FireResist);
break;
}

You can tell that if we continued down this path for too long, things would get a little messy.

But let’s rewind – forget about that fire and frost damage type example. For now, we’ll break these different blocks out into their own methods. To do that, we’ll take advantage of our old friend polymorphism. The first step on our path is to create a few classes – one for each type of damage.

public class PhysicalDamage
{
public int Amount { get; set; }
}public class FrostDamage
{
public int Amount { get; set; }
}public class ShockDamage
{
public int Amount { get; set; }
}public class FireDamage
{
public int Amount { get; set; }
}

And now we’ll just overload the TakeDamage method to deal with each of these individually, and…

public class Hero
{
public int CurrentHitPoints { get; set; }public decimal PhysicalResist { get; set; }
public decimal ShockResist { get; set; }
public decimal FrostResist { get; set; }
public decimal FireResist { get; set; }public void TakeDamage(PhysicalDamage damage)
{
var resist = (int)(damage.Amount * PhysicalResist);
TakeDamage(damage.Amount – resist);
}public void TakeDamage(ShockDamage damage)
{
var resist = (int)(damage.Amount * ShockResist);
TakeDamage(damage.Amount – resist);
}public void TakeDamage(FrostDamage damage)
{
var resist = (int)(damage.Amount * FrostResist);
TakeDamage(damage.Amount – resist);
}public void TakeDamage(FireDamage damage)
{
var resist = (int)(damage.Amount * FireResist);
TakeDamage(damage.Amount – resist);
}private void TakeDamage(int amount)
{
CurrentHitPoints -= amount;
if (CurrentHitPoints <= 0)
Die();
}
}

Presto! Ok, so… now we’ve gotten rid of the switch statement, and isolated each block of logic for calculating damage amounts into their own little methods. So… are we done? Well, not so fast. There’s still an issue.

Any time we need to change any of the damage type calculations, we have to edit methods in the same class. And any time we want to add a new damage type, like we thought about above, we need to create a new class and add a method to the hero class. It just doesn’t look like it really solves our problem.

To help us put all of this together, let’s use a neat little pattern called the Strategy Pattern. You can read up on it yourself, I’ll just jump straight into its application in our scenario. First, we’ll introduce an interface that we will require all of our damage calculation strategies to implement.

public interface ICalculateDamage
{
int CalculateDamage(int amount, Hero hero);
}

And now we’ll introduce implementations of this interface that will handle all of the different ways we can currently calculate damage.

public class PhysicalDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resist = (int)(amount * hero.PhysicalResist);
return amount – resist;
}
}public class ShockDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resist = (int)(amount * hero.ShockResist);
return amount – resist;
}
}public class FrostDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resist = (int)(amount * hero.FrostResist);
return amount – resist;
}
}public class FireDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resist = (int)(amount * hero.FireResist);
return amount – resist;
}
}

These implementations can be used in a class that represents any damage, like this

public class Damage
{
public int Amount { get; set; }
public ICalculateDamage CalculationStrategy { get; set; }public int CalculateAmountFor(Hero hero)
{
return CalculationStrategy.CalculateDamage(Amount, hero);
}
}

And finally, damage can (generically) be dealt to the hero like this

public class Hero
{
public int CurrentHitPoints { get; set; }public decimal PhysicalResist { get; set; }
public decimal ShockResist { get; set; }
public decimal FrostResist { get; set; }
public decimal FireResist { get; set; }public void TakeDamage(Damage damage)
{
CurrentHitPoints -= damage.CalculateAmountFor(this);
if (CurrentHitPoints <= 0)
Die();
}
}

So, has all of this work actually improved our situation? Well let’s take a look. Now, if we need to fix the way in which we calculate a certain kind of damage, we only need to update its corresponding strategy (not the main focus of this post, but it’s a nice bonus example of Single Responsibility). What about if we need to add a new type of damage calculation? Well, that’s as easy as creating a new implementation of our common interface and dealing damage that uses that implementation as its calculation strategy. In other words: the types of damage our hero can take are open to extension, while the hero class itself and other unrelated damage calculation strategies remain closed for modification.

For instance, imagine that we actually introduce a trap that deals damage the way we mentioned earlier, where it chooses the lower of two kinds of resistances and applies damage as if it were that type. That’s as easy as

public class FireOrFrostDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resistanceToApply = Math.Min(hero.FireResist, hero.FrostResist);
var resist = (int)(amount * resistanceToApply);
return amount – resist;
}
}

What about if you wanted to introduce an attack that deals its damage as physical plus half its damage over again as fire? Well, that’s just

public class PhysicalAndHalfFireDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var physicalDamage = amount * (1 – hero.PhysicalResist);
var fireDamage = (amount / 2m) * (1 – hero.FireResist);
return (int)(physicalDamage + fireDamage);
}
}

Let’s get a little more creative – how about if it’s raining, and you want to deal damage only if your hero doesn’t have an umbrella? Well…

public class RainDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
return hero.InventoryContains(Item.Umbrella) ? 0 : amount;
}
}

And if a new requirement comes your way, now that you’ve become familiar with open/closed and the ability to modify behavior without having to change existing code, you can rest assured that

public class NewRequirementsDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
return hero.KnowsAboutOpenClosed ? 0 : amount;
}
}

REFERENCES:

Scythe: https://boardgamegeek.com/boardgame/169786/scythe

Arkham Horror: https://boardgamegeek.com/boardgame/15987/arkham-horror

Gloomhaven: https://boardgamegeek.com/boardgame/174430/gloomhaven

SOLID: https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)

Open/closed principle: https://en.wikipedia.org/wiki/Open/closed_principle

Strategy pattern: https://en.wikipedia.org/wiki/Strategy_pattern

Single Responsibility: http://146.66.113.141/blog/spider-plant-responsibility-principle/

Jay Myers, Test Developer, Software Engineer
March 12, 2018

Writing software can be a lot like being an adventurous hero – or at least that’s what this author tells himself. Luckily for us, one such similarity is highlighted in this post discussing the Open / Closed Principle, which states that software should be open for extension while remaining closed for modification. In other words, you shouldn’t have to change things you’ve already written to add or modify behavior in the future.


APISitting on a table in my apartment is quite a large pile of adventure board games. These are board games that aren’t so much like Monopoly, but a lot more like D&D – full of complex world building and mechanics. Board games like Scythe, Arkham Horror, and Gloomhaven. These games are incredibly fun to play, but sometimes the rules and mechanics at play can become a little confusing. Luckily, if a rule is worded strangely or you have a question about a specific situation you can pretty easily check some online forums to see what others have done in that situation. Occasionally you’ll be lucky and the creator of the game will chime in and clarify. Or, if you’re a certain type of person, you can just ignore a rule or two if they’re inconvenient for you.

It’s a little bit harder to deal with complicated and changing rules when you’re talking about code though. If you’re someone who writes software, or even thinks about software design, then this is not news to you. I’m going to describe a guiding principle that’s pretty important in helping you manage changes to rules in any system you’re creating. Important enough to earn it a letter in the SOLID acronym. It’s the Open/Closed Principle.

Simply put, it just means that it would be a good idea to design things in such a way that if you wanted to modify behavior, you could do that without having to reach into an object and change its code directly. That’s a pretty good idea, but how do you actually do something like that? Well, there’s a lot of different ways to achieve this but I think it’s best served with an example. So let’s invent a problem for ourselves, and then solve it.

The setup for this situation is pretty simple – we want to write code that will represent a hero who is on some kind of quest. The only thing we’ll focus on for this example is the way in which our hero will take damage in battle.

Our hero will have some number of hit points, and if they take sufficient damage (i.e. if their current hit points fall to zero) then they, sadly, die.

public class Hero
{
public int CurrentHitPoints { get; set; }public void TakeDamage(int amount)
{
CurrentHitPoints -= amount;
if (CurrentHitPoints <= 0)
Die();
}
}

So far, so good. But let’s make things a little more interesting. Let’s introduce different types of damage. The universe this journey takes place in will have traps and monsters that are capable of dealing normal physical damage as well as shock, frost, and fire damage.

public enum DamageType
{
Physical,
Shock,
Frost,
Fire
}

Luckily, our hero has innate resistances to each of these different types of damage. Those resistances are going to be defined as a percentage by which damage of that type should be reduced. So if we introduce this concept into our existing hero, maybe it looks something like this.

public class Hero
{
public int CurrentHitPoints { get; set; }public decimal PhysicalResist { get; set; }
public decimal ShockResist { get; set; }
public decimal FrostResist { get; set; }
public decimal FireResist { get; set; }public void TakeDamage(int amount, DamageType type)
{
var damageAmount = amount;
switch (type)
{
case DamageType.Physical:
damageAmount -= (int)(amount * PhysicalResist);
break;
case DamageType.Shock:
damageAmount -= (int)(amount * ShockResist);
break;
case DamageType.Frost:
damageAmount -= (int)(amount * FrostResist);
break;
case DamageType.Fire:
damageAmount -= (int)(amount * FireResist);
break;
}CurrentHitPoints -= damageAmount;
if (CurrentHitPoints <= 0)
Die();
}
}

Ok, so… that’s not so bad. It’s not great, but it’s not terrible either. The main problem we are facing right now (other than the fact that our hero cannot heal themselves) is that switch statement. It might look harmless, but think about what might happen if we need to add to it.

Let’s say that we encounter a new kind of trap that will deal both frost and fire damage – and we should only use the lower of those two resistances to calculate the damage. You would need to add a new case to that switch statement, and introduce a new damage type (even though it’s not really a new kind of damage). So our switch statement would end up looking like this:

switch (type)
{
case DamageType.Physical:
damageAmount -= (int)(amount * PhysicalResist);
break;
case DamageType.Shock:
damageAmount -= (int)(amount * ShockResist);
break;
case DamageType.Frost:
damageAmount -= (int)(amount * FrostResist);
break;
case DamageType.Fire:
damageAmount -= (int)(amount * FireResist);
break;
case DamageType.FireAndFrostButYouTakeTheLowestResistance:
damageAmount -= (int)(amount * Math.Min(FrostResist, FireResist);
break;
}

You can tell that if we continued down this path for too long, things would get a little messy.

But let’s rewind – forget about that fire and frost damage type example. For now, we’ll break these different blocks out into their own methods. To do that, we’ll take advantage of our old friend polymorphism. The first step on our path is to create a few classes – one for each type of damage.

public class PhysicalDamage
{
public int Amount { get; set; }
}public class FrostDamage
{
public int Amount { get; set; }
}public class ShockDamage
{
public int Amount { get; set; }
}public class FireDamage
{
public int Amount { get; set; }
}

And now we’ll just overload the TakeDamage method to deal with each of these individually, and…

public class Hero
{
public int CurrentHitPoints { get; set; }public decimal PhysicalResist { get; set; }
public decimal ShockResist { get; set; }
public decimal FrostResist { get; set; }
public decimal FireResist { get; set; }public void TakeDamage(PhysicalDamage damage)
{
var resist = (int)(damage.Amount * PhysicalResist);
TakeDamage(damage.Amount – resist);
}public void TakeDamage(ShockDamage damage)
{
var resist = (int)(damage.Amount * ShockResist);
TakeDamage(damage.Amount – resist);
}public void TakeDamage(FrostDamage damage)
{
var resist = (int)(damage.Amount * FrostResist);
TakeDamage(damage.Amount – resist);
}public void TakeDamage(FireDamage damage)
{
var resist = (int)(damage.Amount * FireResist);
TakeDamage(damage.Amount – resist);
}private void TakeDamage(int amount)
{
CurrentHitPoints -= amount;
if (CurrentHitPoints <= 0)
Die();
}
}

Presto! Ok, so… now we’ve gotten rid of the switch statement, and isolated each block of logic for calculating damage amounts into their own little methods. So… are we done? Well, not so fast. There’s still an issue.

Any time we need to change any of the damage type calculations, we have to edit methods in the same class. And any time we want to add a new damage type, like we thought about above, we need to create a new class and add a method to the hero class. It just doesn’t look like it really solves our problem.

To help us put all of this together, let’s use a neat little pattern called the Strategy Pattern. You can read up on it yourself, I’ll just jump straight into its application in our scenario. First, we’ll introduce an interface that we will require all of our damage calculation strategies to implement.

public interface ICalculateDamage
{
int CalculateDamage(int amount, Hero hero);
}

And now we’ll introduce implementations of this interface that will handle all of the different ways we can currently calculate damage.

public class PhysicalDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resist = (int)(amount * hero.PhysicalResist);
return amount – resist;
}
}public class ShockDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resist = (int)(amount * hero.ShockResist);
return amount – resist;
}
}public class FrostDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resist = (int)(amount * hero.FrostResist);
return amount – resist;
}
}public class FireDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resist = (int)(amount * hero.FireResist);
return amount – resist;
}
}

These implementations can be used in a class that represents any damage, like this

public class Damage
{
public int Amount { get; set; }
public ICalculateDamage CalculationStrategy { get; set; }public int CalculateAmountFor(Hero hero)
{
return CalculationStrategy.CalculateDamage(Amount, hero);
}
}

And finally, damage can (generically) be dealt to the hero like this

public class Hero
{
public int CurrentHitPoints { get; set; }public decimal PhysicalResist { get; set; }
public decimal ShockResist { get; set; }
public decimal FrostResist { get; set; }
public decimal FireResist { get; set; }public void TakeDamage(Damage damage)
{
CurrentHitPoints -= damage.CalculateAmountFor(this);
if (CurrentHitPoints <= 0)
Die();
}
}

So, has all of this work actually improved our situation? Well let’s take a look. Now, if we need to fix the way in which we calculate a certain kind of damage, we only need to update its corresponding strategy (not the main focus of this post, but it’s a nice bonus example of Single Responsibility). What about if we need to add a new type of damage calculation? Well, that’s as easy as creating a new implementation of our common interface and dealing damage that uses that implementation as its calculation strategy. In other words: the types of damage our hero can take are open to extension, while the hero class itself and other unrelated damage calculation strategies remain closed for modification.

For instance, imagine that we actually introduce a trap that deals damage the way we mentioned earlier, where it chooses the lower of two kinds of resistances and applies damage as if it were that type. That’s as easy as

public class FireOrFrostDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var resistanceToApply = Math.Min(hero.FireResist, hero.FrostResist);
var resist = (int)(amount * resistanceToApply);
return amount – resist;
}
}

What about if you wanted to introduce an attack that deals its damage as physical plus half its damage over again as fire? Well, that’s just

public class PhysicalAndHalfFireDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
var physicalDamage = amount * (1 – hero.PhysicalResist);
var fireDamage = (amount / 2m) * (1 – hero.FireResist);
return (int)(physicalDamage + fireDamage);
}
}

Let’s get a little more creative – how about if it’s raining, and you want to deal damage only if your hero doesn’t have an umbrella? Well…

public class RainDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
return hero.InventoryContains(Item.Umbrella) ? 0 : amount;
}
}

And if a new requirement comes your way, now that you’ve become familiar with open/closed and the ability to modify behavior without having to change existing code, you can rest assured that

public class NewRequirementsDamageCalculation : ICalculateDamage
{
public int CalculateDamage(int amount, Hero hero)
{
return hero.KnowsAboutOpenClosed ? 0 : amount;
}
}

REFERENCES:

Scythe: https://boardgamegeek.com/boardgame/169786/scythe

Arkham Horror: https://boardgamegeek.com/boardgame/15987/arkham-horror

Gloomhaven: https://boardgamegeek.com/boardgame/174430/gloomhaven

SOLID: https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)

Open/closed principle: https://en.wikipedia.org/wiki/Open/closed_principle

Strategy pattern: https://en.wikipedia.org/wiki/Strategy_pattern

Single Responsibility: http://146.66.113.141/blog/spider-plant-responsibility-principle/