Home Refactoring Techniques: Lookup Tables

Enter a search term to find articles.
refactoring-techniques-lookup-tables
2022-05-14
963

Refactoring Techniques: Lookup Tables

One of the easiest and most common techniques you can reach out for when refactoring your code is a Lookup Table.

A lookup table is basically just a table, can be objects, arrays, database tables, redis cache or whatever, that you can look in to find values. Let's take a look at some common refactoring examples.

Refactoring multiple 'OR's

For our first example, let's say that we have a User model that has a type property.

// Imagine you have something like this...
if ($user->type === 'administrator' || $user->type === 'teacher' || $user->type === 'guardian') {
        // Do something here...
}

You can image if in the future we need to add more user type that the condition could get long. This is one perfect candidate where we can use lookup tables. Take a look...

// Put our values in a lookup table, then proceed with our condition...
$table =  ['administrator', 'teacher', 'guardian'];

if (in_array($user->type, $table)) {
        // Do something here...
}

We've refactored to a lookup table, in this case an array, to group our possible values. And then used the lookup table in our condition to check against the user type. With this refactor, should we need to add more user types, all we have to change is our lookup table. This is what we call isolating the change.

And with Laravel, we can refactor this further to be much more readable, like so...

$table =  ['administrator', 'teacher', 'guardian'];

if(collect($table)->contains($user->type)) {
        // Do something here...
}

Using the collection the condition becomes much more clearer that we are checking if the table contains the given user type. 👌

Imperative vs Declarative Programming

Using the same example above, imagine a scenario that when you login a user, you need to redirect them to their dedicated homepage based on their user type. You would probably have something like this...

public function redirectUser($user) 
{
        if ($user->type === 'administrator') {
                return redirect('/administrator/dashboard');
        } elseif ($user->type === 'teacher') {
                return redirect('/teachers/schedules');
        } elseif ($user->type === 'guardian') {
                return redirect('/guardians/learners');
        }
}

As you can see, this approach is kind of describing the step by step on how to determine where to redirect the user based on their type. When you are specifying the exact steps to get the results, this is what we call imperative programming.

Now maybe your first instinct is to refactor using switch statements. While that would definitely work, I would say switch statements would still somehow describe the step by step of the process. Using a lookup table would be much more cleaner. Let's take a look...

public function redirectUser($user) 
{
        $homepages = [
                'administrator' => '/administrator/dashboard',
                'teacher' => '/teachers/schedules',
                'guardian' => '/guardians/learners',
        ];

        return redirect($homepages[$user->type]);
}

As you can see with this approach, its much more results-focused. It is able to determine the outcome without having to describe the process step by step. This is what we call declarative programming. 😎

Generally speaking, it's easier for human brain to describe step by step approach instead of results-based approach especially when writing alorithms. This is why declarative programming is often a result of a refactor.

As Strategy Pattern

This is almost the same as the last example, but you can use lookup tables to determine strategy that you can use for certain logic.

To illustrate, using the same user type example above, imagine if we have different strategies for processing payments based on the user type...

public function processPayment($user)
{
        $handler;

        if ($user->type === 'administrator') {
                $handler = new BankPaymentHandler;
        } elseif ($user->type === 'teacher') {
                $handler = new GcashPaymentHandler;
        } elseif ($user->type === 'guardian') {
                $handler = new PaymayaPaymentHandler;
        }

        // Assuming the handler classes are abiding by a contract with 
        // a process method that accepts an instance of the user...
        return $handler->process($user);
}

Again, we can refactor to a more declarative programming approach using a lookup table, like so...

public function processPayment($user)
{
        $strategies = [
                'administrator' => BankPaymentHandler::class,
                'teacher' => GcashPaymentHandler::class,
                'guardian' => PaymayaPaymentHandler::class,
        ];

        return (new $strategies[$user->type])->process($user);
}

Hope you've learned how to use lookup tables to refactor your code. Happy coding!

Marvin Quezon

Full Stack Web Developer
Marvin Quezon · Copyright © 2024 · Privacy · Sitemap