The Hierarchy of Complexity for Conditionals
Published: 2017-03-01 18:34:14

---
title: The Hierarchy of Complexity for Conditionals
published: true
description: 
cover_image: 
tags: 
canonical_url: https://medium.com/extreme-programming/the-hierarchy-of-complexity-for-conditionals-f6763b23f76e#.dxqegbps1
---
[_Originally posted on Extreme Programming_]
(https://medium.com/extreme-programming/the-hierarchy-of-complexity-for-conditionals-f6763b23f76e#.dxqegbps1)

When the Ruby on Rails framework was first released, one of its biggest selling points was [convention over configuration](http://rubyonrails.org/doctrine/#convention-over-configuration). Name your ActiveRecord model **User**, and it would use the **users** table in its generated SQL. This preempted the need for verbose configuration. Instead all you had to know was a handful of rules and your codebase would be predictable and consistent throughout.

It turns out this technique can be utilized in your own code. In her instant classic [_Practical Object Oriented Design in Ruby_](http://www.poodr.com/), Sandi Metz posed the following example of a case statement which follows an obvious pattern (see shock.rb at top).

```ruby
# Source: https://www.youtube.com/watch?v=f5I1iyso29U&t=23m44s
class Shock
  def cost(type)
    case type
    when :front
      FrontShockCost.new.compute
    when :rear
      RearSHockCost.new.compute
    when :lefty
      LeftyShockCost.new.compute
    else
      ShockCost.new.compute
    end
  end
end
```
```ruby
class Shock
  def cost(type)
    "#{type.to_s.titleize}Shock".constantize.new.compute
  end
end
```

Sandi's observation is that this code has to change any time you add or remove a type of shock. This increases the difficulty of changing code (see the [Shotgun Surgery](https://sourcemaking.com/refactoring/smells/shotgun-surgery) code smell). **Shock** has too much “Meta Knowledge”. **Shock** knows these four things.

1\. cost method  
2\. four shock cost types  
3\. four shock cost classes  
4\. which type gets what class (the mapping from type to class)

We can reduce this information by replacing these rules with a _convention_, as shown in the second file. Sandi [demonstrated this](https://www.youtube.com/watch?v=f5I1iyso29U&t=23m44s) in one of her talks, saying:

> This transition where you make up a class name and turn it into the object..is something that it's really useful to get comfortable with.

Some software developers I've encountered eschew this approach based on concerns like the difficulty of searching for calling code. While I think that concern has merit, the value of this refactoring adds up the more examples you have and/or the more frequently the code changes. I don't think one should reject this (or any) programming approach on _principle_. As with any business decision, it should be about a cost/benefit calculation. _When_ is it worth it to apply a convention? How many conditional branches (which ideally just duplicate the same convention) must you obviate before the abstraction is worth the cost in terms of searchability?

That said, it's nice to know our alternatives. I propose something like a “hierarchy of complexity” for conditionals. It runs from simplest and least flexible to most verbose and flexible.

**Create a convention.**
As demonstrated above, this says there's one rule to follow, whether you have three unique cases or a thousand. This is definitively the simplest approach (simple in the [Rich Hickey sense](https://www.infoq.com/presentations/Simple-Made-Easy)).

**Use a map.**
A map (like the Ruby hash below) is a little more verbose, and doesn't enforce a rule about naming conventions. Thus it can lead to an increase in information–multiple one-off rules instead of a single overarching rule forming a convention. But it does enforce _part_ of the rule–the interface. Each class is expected to provide the **compute** method. This doesn't create a conditional into which future programmers might “lazily” (or simply _unwittingly_) shovel additional code for each specific use case. (Sandi Metz even makes the point that you can replace an **if/else** with a map, where the keys are **true** and **false**.)


```ruby
shock_cost_class_map = {
  front: FrontShockCost,
  lefty: LeftyShockCost,
  rear: RearSHockCost,
}
shock_cost_class = shock_cost_class_map.fetch(type, ShockCost)
shock_cost_class.new.compute
```
**Use a case statement.**
Looking back at our original example above, we see that each conditional is simply a match between a type variable and a value. This is simpler (less flexible) than a series of arbitrary boolean expressions, but it still creates the tempting opportunity for a future programmer to insert new behaviors into one or more of those case statements rather than merely following the rule that each class's **compute** method does the relevant work. As OO practitioners we eschew differences and seek sameness where possible.

**Use arbitrary boolean expressions (if/else).**
This is most flexible and, in my view, the last resort. Because not only does this give us the flexibility to change behavior for each condition (e.g. replacing the class with inlined code), but it also gives us infinite flexibility in the _comparisons_. Any boolean expression will do.

Whenever I need conditional behavior, I start at the top of this hierarchy, and work my way down only when my requirements force me to.