Patterns & Practices

Cool Patterns: Hole in the Middle

It’s been a while since I’ve written a post on here. But hey, new year, new post!

In this new series, I’ll be taking a look at some useful patterns and practices that I’ve encountered in my day-to-day work that might be of use to folks out there! Today’s topic: “Hole in the middle”, a type of Inversion of Control (IoC).

The Scenario

I’m writing endpoints to handle user management in my CRUD app, and I need to check a few things before performing my CRUD operation. Let’s take a look at our controller:

public class UserController: ApiController
{
  public User GetUser (User user, UserDataOptions options)
  {
    if (!actingUser.HasPermission(UserPermissions.ManageUsers))
    {
      throw new NoAccessException();
    }

    if (!FeatureManagement.IsUserManagementEnabled())
    {
      throw new FeatureNotEnabledException();
    }

    User userToReturn = UserManager.GetUser(user.Id);
    userToReturn.ApplyUserDataOptions(options);
    return userToReturn;
  }

  public User CreateUser (User user, UserDataOptions options)
  {
    if (!actingUser.HasPermission(UserPermissions.ManageUsers))
    {
      throw new NoAccessException();
    }

    if (!FeatureManagement.IsUserManagementEnabled())
    {
      throw new FeatureNotEnabledException();
    }

    User userToReturn = UserManager.GetUser(user.Id);

    if (userToReturn == null)
    {
      userToReturn = UserManager.CreateUser(user);
    }

    userToReturn.ApplyUserDataOptions(options);
    return userToReturn;
  }

  public User UpdateUser (User user, UserDataOptions options)
  {
    if (!actingUser.HasPermission(UserPermissions.ManageUsers))
    {
      throw new NoAccessException();
    }

    if (!FeatureManagement.IsUserManagementEnabled())
    {
      throw new FeatureNotEnabledException();
    }

    User userToReturn = UserManager.GetUser(user.Id);

    if (userToReturn == null)
    {
      throw new UserNotFoundException();
    }
    User userToReturn = UserManager.UpdateUser(user);

    userToReturn.ApplyUserDataOptions(options);
    return userToReturn;
  }
}

Good engineering practice dictates, “If you find yourself writing the same code three or more times, you should refactor.” Generally speaking, that’s good advice, but sometimes it isn’t completely clear how to refactor the code to reduce duplication.

The Obvious Refactoring

We can obviously see that the permission check and the check to see if user management is enabled can be refactored out into a helper method that each of our three controller methods can call.

public class UserController: ApiController
{
  public User GetUser (User user, UserDataOptions options)
  {
    CheckPermissionAndFeatureStatus();
    User userToReturn = UserManager.GetUser(user.Id);
    userToReturn.ApplyUserDataOptions(options);
    return userToReturn;
  }

  public User CreateUser (User user, UserDataOptions options)
  {
    CheckPermissionAndFeatureStatus();

    User userToReturn = UserManager.GetUser(user.Id);

    if (userToReturn == null)
    {
      userToReturn = UserManager.CreateUser(user);
    }

    userToReturn.ApplyUserDataOptions(options);
    return userToReturn;
  }

  public User UpdateUser (User user, UserDataOptions options)
  {
    CheckPermissionAndFeatureStatus();

    User userToReturn = UserManager.GetUser(user.Id);

    if (userToReturn == null)
    {
      throw new UserNotFoundException();
    }
    User userToReturn = UserManager.UpdateUser(user);

    userToReturn.ApplyUserDataOptions(options);
    return userToReturn;
  }
  private void CheckPermissionAndFeatureStatus()
  {
    if (!actingUser.HasPermission(UserPermissions.ManageUsers))
    {
      throw new NoAccessException();
    }

    if (!FeatureManagement.IsUserManagementEnabled())
    {
      throw new FeatureNotEnabledException();
    }
  }
} 

Ok, so that’s better, but we still have some code duplication. Namely, we see that after we perform the operation that the controller method is supposed to perform, we call ApplyUserDataOptions and then return the User object. We could refactor that out, but it wouldn’t really help us too much. If only there was a way to write some code once and then execute different code in the middle depending on where we’re calling it…

Enter callbacks the Hole in the Middle

If you’re familiar with callbacks in Javascript, the refactoring might be obvious: Have one method that checks the permission and feature status, calls a function that takes in a User object and returns a User object, applies the UserDataOptions, and returns the User object. The function that takes in a User object and returns a User object is our “hole in the middle” that each of our CRUD methods will provide when they’re called. Let’s take a look at the new code:

public class UserController: ApiController
{
  public User GetUser (User user, UserDataOptions options)
  {
    return PerformUserManagementOperation(user, options, null, (userWhoExists) => userWhoExists);
  }

  public User CreateUser (User user, UserDataOptions options)
  {
    return PerformUserManagementOperation(user, options, (userWhoDoesNotExist) => UserManager.CreateUser(user), (userWhoExists) => userWhoExists);
  }

  public User UpdateUser (User user, UserDataOptions options)
  {
    return PerformUserManagementOperation(user, options, (userWhoDoesNotExist) => throw new UserNotFoundException(), (userWhoExists) => UserManager.UpdateUser(user));
  }

  private User PerformUserManagementOperation(User inputUser, UserDataOptions options, Func<User, User> userDoesNotExistOperation, Func<User, User> userExistsOperation)
  {
    if (!actingUser.HasPermission(UserPermissions.ManageUsers))
    {
      throw new NoAccessException();
    }

    if (!FeatureManagement.IsUserManagementEnabled())
    {
      throw new FeatureNotEnabledException();
    }

    User user = UserManager.GetUser(inputUser.Id);

    if (userInSystem == null && userDoesNotExistOperation != null)
    {
      user = userDoesNotExistOperation(inputUser);
    }
    else if (userExistsOperation != null && userExistsOperation != null)
    {
      user = userExistsOperation(user);
    }

    user?.ApplyUserDataOptions(options);
    return userToReturn;
  }
} 

It’s fairly easy to see the reduction in duplication here. Since each of the three user management methods followed the same general pattern, we could pull out that similar code and fill in the hole in the middle of the method where the custom logic for each user management method should be. Pretty neat, huh?

So what is all this about Inversion of Control, then? IoC is essentially separating the what to do from when we do that thing. Contrary to what you may hear elsewhere, IoC is not just another fancy way of talking about Dependency Injection. In the above example, we don’t know when or even if PerformUserManagementOperation will invoke our lambdas. However, since we know the conditions under which the provided functions will be invoked, we can write the appropriate code for each user management operation should those conditions be met. This way, each specific operation method contains only the code needed to Get, Update, or Create the user while all the other shared boilerplate is abstracted away.

Of course, while this post uses C#, the general pattern can be used in any language where function pointers (or similar types) are supported. Hopefully you can make some good use of this pattern where it can simplify your logic!

Edit: Hat Tip to u/moocat on Reddit for letting me know I had missed a null check where the userExistsOperation will be called even if no userExistsOperation is passed in.

7 thoughts on “Cool Patterns: Hole in the Middle

  1. it looks like you have ( userExistsOperation != null ) twice.
    but, if you remove the null and as it’s private you know it’s not a null.rename user Exist, you get

    return PerformUserManagementOperation(user, options, n => null, u => u);
    ….

    if (userInSystem == null )
    {
    user = userDoesNotExistOperation(inputUser);
    }
    user = userOperation(user);

    1. This was meant to be more of a pseudo-code type example where actingUser represents the user performing the action. You can liken that to User (which, to be honest, I should have used to eliminate confusion).

      1. That’s what I thought. I personally prefer the second refactoring, as the third refactoring doesn’t feel right – not to mention it doesn’t compile.

        If I had this situation in production code, I’d look at using a custom attribute on the controller. That is much cleaner IMO.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s