Scenario Weaving – a Software Development Antipattern

In Software Engineering, there is a term called Cyclomatic Complexity. It is a measurement of the number of branches in a method. There is a huge correlation between high Cyclomatic Complexity and bugs.

Today, I want to discuss an antipattern that leads to high Cyclomatic Complexity: Scenario weaving.

What is Scenario Weaving?

Scenario weaving is when a method handles multiple scenarios. Yes, this clearly breaks the Single Responsibility Principle.

Any time you see multiple “if” conditions in a method, you can be sure that the code is likely doing scenario weaving.

If you see a switch/case statement, you can be 100 sure that the code is doing scenario weaving as each case is a scenario. By the way, you should almost never be using the switch/case statement anymore. See  What to do instead of using switch/case.

Scenario Weaving Example

This is a bad, antipattern example of scenario weaving.

This is simple example Cookie Monster code. For each cookie type, have the closest monster that likes that cookie type eat it.

public void EatCookie(Cookie cookie)
{
    if (cookie.Type == "ChocolateChip")
    {
        if (chocolateMonster.IsCloser())
           chocolateMonster.Eat(cookie);
        else if (cookieMonster.IsCloser())
           cookieMonster.Eat(cookie);
    }
    if (cookie.Type == "MacadamianNut")
    {
        if (nutMonster.IsCloser())
            nutMonster.Eat(cookie);
        else if (cookieMonster.IsCloser())
            cookieMonster.Eat(cookie);
    }
    // ...
}

What type of cookie and which monster is closer can create multiple scenarios. As you can see, this method is trying to handle all the scenarios. Multiple scenarios have been weaved together in this method. To add more scenarios, our code just gets more and more complex. It breaks every letter of SOLID, forces us to repeat code a lot breaking DRY and really becomes a beast to unit test. The more cookies, flavors, etc., the more complex this method gets.

Scenario Weaving Refactored Example

This is a good, best-practice pattern example of code that doesn’t use scenario weaving and has low cyclomatic complexity.
This is simple example Cookie Monster code. For each cookie type, have the closest monster that likes that cookie type eat it.

public class MonsterSelector : IMonsterSelector // Interface omitted for brevity.
{
    // Maps each cookie type to the monsters that like that cookie type. Inject this in the constructor.
    Dictionary<string, List<Monster>> _monstersByCookieType; 

    // Constructor - omitted for bevity

    public IMonster SelectBy(ICookie cookieT)
    {
        var monsterList = _monstersByCookieType[cookie.Type];
        return monsterList.SelectClosestMonster();
    }
}

public void EatCookie(Cookie cookie)
{
    var monster = monsterSelector.SelectBy(cookie);
    monster.Eat(cookie);
}

What type of cookie and which monster is closer still creates the same multiple scenarios. However, the above code handles and infinite number of scenarios without change and without increasing complexity. Also, this code is really easy to unit test.

Yes, more cookie types can exist. Yes, more monsters can exist. Yes, monsters can be at various distances from a cookie. Doesn’t matter. The code works in every scenario.

Now, in this above naive case, we solved all scenarios with one piece of SOLID code. However, sometimes you might have specific code per scenario. Imagine that you have some Monsters that eat in a special way and inherit from the IMonster interface . You would have to write there special Eat method separately for each special monster.

The important concepts here:

  1. Create a scenario selector.
  2. Have code that handles each scenario and make sure that code all implements the same scenario handling method signature (i.e. a scenario handling interface).

Conclusion

Scenario weaving is an antipattern that leads to complex buggy code, where the bugs can affect multiple scenarios. Such code leads to low quality code that breaks SOLID and DRY principles and is difficult to unit test.

Scenario selecting and having separate code to handle each scenario leads to bug free code, or at least a bug only affects one scenario. Such code leads to high quality code that follows SOLID and DRY principles and is easy to unit test.

One Comment

Leave a Reply

How to post code in comments?