Replace Conditions with Instructions

Hello!

This pattern is loosely based on Martin Fowler’s article on Adaptive Models.

I often find that my code needs to handle a bunch of different cases. It can be tempting (but ultimately painful) to just write a bunch of if conditions to handle those cases. Imagine converting HTML files into markdown:

file_names = [
    'a.md',
    'a.html',
    'b.html'
]
 
def should_convert(file_name):
    extension = file_name.split('.')[1]
    if extension == 'md':
        return False
    elif extension == 'html':
        return True
 
def convert(file_name):
    print(f'{file_name} > converted_{file_name}')
 
if __name__ == '__main__':
    for file_name in file_names:
        if should_convert(file_name):
            convert(file_name)

We have to write logic that understands each case. This simplified example doesn’t look too bad, but in real life it’ll be worse.

Instead, I like to write an object that encodes the right action for each case and then look up that action as each case is processed:

should_convert = {
    'md': False,
    'html': True
}
 
file_names = [
    'a.md',
    'a.html',
    'b.html'
]
 
def convert(file_name):
    print(f'{file_name} > converted_{file_name}')
 
if __name__ == '__main__':
    for file_name in file_names:
        if should_convert[file_name.split('.')[1]]:
            convert(file_name)

Replacing the should_convert() function with a dictionary simplifies several things:

  • There’s less code to understand. We don’t have to read through implementation for every case, we just read through the cases.
  • To support new cases, we just update the dictionary. We don’t have to write new code.
  • Because we don’t have to write new code to support new cases, we don’t need to write tests to assert the new code works. The logic is the same, it just processes more instructions.

This also handles errors cleanly. Suppose we add a file with an extension we don’t know if we should convert (.doc) and run the script:

Traceback (most recent call last):
  File "./adaptive.py", line 18, in <module>
    if should_convert[file_name.split('.')[1]]:
KeyError: 'doc'

It’s immediately clear that it didn’t know if it should convert the .doc extension.

If you encode desired behavior into objects, you only need to write code that can follow those instructions. Here’s the principle I try to follow:

Conditions are for processing instructions, not for implementing them.

This has simplified a ton of my code. I highly recommend it.

Happy automating!

Adam

Need more than just this article? We’re available to consult.

You might also want to check out these related articles: