Using the Strategy Pattern to Reduce Complexity in Your JavaScript

Problem:

Do you ever find yourself writing a lengthy switch statements or if statements structured like a switch. Luckily there is the Strategy Pattern to help alleviate this complex, sometimes unintelligible code. You may have also noticed that this code can have quite a high Cyclomatic Complexity.

Let's take a look at an example of a lengthy switch statement. The code below is checking a user's role and if they have read or write permissions. Now this is a contrived example but the pattern still applies.

JS Bin

Here we can see the complexity is becoming unweildy, sure there are ways to clean this code up more with string manipulation of the urls and roles/permissions. The permissions check should be a single function call as well. Humor me and let's pursue the Strategy Pattern for now and we'll clean up the permissions check along the way.

Solution:

The Strategy Pattern will be our weapon of choice for tackling the problem above. So what is the Strategy Pattern, you may ask? Essentially it is a way to evaluate an input at runtime and execute predefined code based off of that input. This is JavaScript, a dynamic language so we won't have to go the full polymorphic, implenting interfaces like in C# to use this pattern. As an aside, you could actually do it that way in JavaScript if you'd like. Check out this implentation to see how to do it that way.

For our solution we are going to utilize the fact that the JavaScript Object is essentially a dictionary, note the bracket notation to get object properties by a string. We are not handling super complex logic so a dictionary will suffice as our strategy implementation.

Implementation

First we will start off laying out the basic structure for our strategy. We will define a UserRoleStrategy class with functions for each role and a default handler.

We are creating a function to handle each user role, similar to our switch statement. Now let's go ahead and add the logic for each user role. We will pull the logic out of our switch and place it in the appropriate user role function.

That's still pretty messy, so let's refactor the permissions logic. This step is not strategy pattern related but will make the code more readable. Our strategy object allows us to refactor easily and contain the logic within it.

JS Bin

Refactor complete, now we have a strategy object with two private functions to handle our permission/url logic and our logging. We no longer rely on a switch statement to handle our user role logic but a full blown object that can be extended as needed. Note on line 53 we are instantiating our strategy object, currentRoleHandler, and passing in the user. We then use the square brackets to pass in our user role on line 57, currentRoleHandler[user.role](). Resulting in a call to the correct function on our strategy object.

That's it, thanks for reading!

tl;dr

Reduce cyclomatic complexity in your JavaScript with the Strategy Pattern and utilize the fact that JavaScript objects can be used as a dictionaries.


About Sean Biefeld

Hello all, my name is Sean Biefeld. I graduated from Baylor University with a BBA in Management Information Systems. I am currently working for Headspring . I have been a developing software professionally since 2004. My primary development focus is on HTML, CSS, JavaScript and C#. This blog is a forum for me to post my experiences, ideas, rants, and thoughts as I traverse the mountain of life.
This entry was posted in Javascript, Readability and tagged , , . Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Rasmus Bækgaard

    Very nice.
    Note: line 53 is ‘};’ …

    • Sean Biefeld

      Ya, some reason github’s gist line numbers aren’t lining up correctly, seems be some conflicting css or something, haven’t dug into it.

    • seanbiefeld

      Yeah, github’s gist line numbers are not behaving for some reason. :-

  • I think alot of your post’s value was eaten up by the tomfoolery with JS variable declarations. It is very nonobvious to what you’re doing, so nonobvious you specifically have to call out line 53 (which your formatting doesn’t align with).

    This post would have much higher clarity if you just separated things reasonably.

    var UserRoleStrategy = function (user) { … }

    var user = { …. }

    currentRoleHandler = new UserRoleStrategy(user);
    //check that the role is has a strategy
    if (currentRoleHandler[user.role]) { …..

  • This is being pedantic but i would not name UserRoleStrategy as such. The fact this combines multiple strategies it is not truly a strategy. this.devops() is a strategy. If I group multiple strategies into 1 file i tend to call it a “Policy” or “RuleSet” etc.

    • seanbiefeld

      Thanks, I may have to rename it.

  • Ohnoez

    This is NOT a strategy pattern, it is simply a dictionary.

    A strategy leaves a single implementation to a single object conforming to an interface, upon which a context item defines which strategy it will use. Your “alternative” example link is more accurate.

    You do neither here.

    • seanbiefeld

      Thanks, should I reclassify it as pseudo-strategy?

      • Ohnoez

        No, this is a complete miss on a strategy pattern.

        Like I said, this is literally and simply the dictionary/hash pattern.

        • seanbiefeld

          I agree that the structure being used is a dictionary/hash, however the pattern being applied is Strategy.

          • I concur, all the right pieces are there. At this point it becomes more of an argument of names and where to arrange the deck chairs.

    • I think you’re being too rigid. It is a dictionary, but it is a dictionary of actions. That makes the interface in this case IDoStuff { DoStuff() } where it’s all implicit with the looseness of javascript.

      I think comment really ties into mine that the naming isn’t quite right. this.devops() is indeed a strategy.

      • Ohnoez

        A strategy is only a strategy on when and why it’s used. Otherwise, every functional block is a strategy by that logic.

        • If all of your code adheres to the interface IDoStuff { DoStuff() }, then sure. Otherwise, no.

      • Ohnoez

        Also, the point of patterns is rigidity.

    • My intimate pictures here ….

      ¤—–„>ׂ…—„——→›→•¸•¬¬±

  • Manny Fleurmond

    Isn’t this also similar to states?