HomeContact
Better Code Series Tip #4: Abstract long conditionals!
Software Design
Better Code Series Tip #4: Abstract long conditionals!
Kodi O'Neil
Kodi O'Neil
December 20, 2022
2 min

Table Of Contents

01
Messy Conditionals
02
Boolean Variable
03
Boolean Function
04
Conclusion

Messy Conditionals

Sometimes handling conditional logic can get messy.

Checking against various conditions in the same statement can eat up a lot of space. It can be hard to read. And if we’re dealing with already lengthy syntax from long variable names your code formatter (which you are using because you are an efficient developer) might do something like this.

const validateForm = (formData) => {
  if (
    !formData.username ||
    !formData.username.length > 0 ||
    !formData.username.match(/\^[a-zA-Z]$/)
  ) {
    return alert("Invalid username!");
  }
};

Now this isn’t the worst. In fact while I was writing this example I realized prettier handles lengthy JavaScript pretty well- I’ll spare you the same example in Ruby.

But like many things in software design “not being the worst” is not the bar we’re shooting for. If we add more logic to the validateForm function we’ll start to see the issue with having such lengthy conditionals.

const validateForm = (formData) => {
  if (
    !formData.username ||
    !formData.username.length > 9 ||
    !formData.username.match(/\^[a-zA-Z]$/)
  ) {
    return alert("Invalid username!");
  }

  if (
    !formData.email ||
    !formData.email.length > 5 ||
    !formData.email.match(/\[a-zA-Z0-9]@[a-z].[a-z]/)
  ) {
    return alert("Invalid email!");
  }

  if (!formData.firstName || !formData.firstName.length > 1) {
    return alert("Invalid first name!");
  }

  if (!formData.lastName || !formData.lastName.length > 1) {
    return alert("Invalid last name!");
  }
};

There’s two ways we can fix this issue.

Boolean Variable

One simple way to deal with lengthy conditionals is to instantiate a variable.

const validateForm = (formData) => {
  const usernameInvalid =
    !formData.username ||
    !formData.username.length > 9 ||
    !formData.username.match(/\^[a-zA-Z]$/);
    
  if (usernameInvalid) {
    return alert("Invalid username!");
  }
};

This abstraction allows us to simplify the if-statement. Now when you read the logic in this function it’s in plain English and you’ll have no trouble understanding it.

In some cases this is all that’s needed. Depending on the scope you’re working with you can even store boolean variables outside of the function itself.

For example, if we have a simple React component with formData as our state.

const UserSignupForm = () => {
  const [formData, setFormData] = useState({ username: "" });

  const usernameInvalid =
    !formData.username ||
    !formData.username.length > 9 ||
    !formData.username.match(/\^[a-zA-Z]$/);

  const validateForm = () => {
    if (usernameInvalid) {
      return alert("Invalid username!");
    }
  };

  return <ImaginaryJSX onSubmit={validateForm}/>;
};

The validateForm function becomes even simpler!

Boolean Function

Sometimes our conditionals are more complicated and we need to perform more logical operations to see if a condition is true.

This is where writing a function that acts as a validator/conditional check can be helpful.

For example if we wanted to ensure a username is not currently in use while we are validating it a separate validation function starts to make more sense than a variable.

/**
 * Retrieves a user by a given username.
 * @param {string} username
 * @return {(Object|null)}
 */
const getUserByUsername = (username) => {
  axios
    .get(`/users?username=${username}`)
    .then((res) => {
      const user = res.data;
      return user;
    })
    .catch((err) => console.log(err));
};

const usernameInvalid = (username) => {
  const isInvalid =
    !formData.username ||
    !formData.username.length > 9 ||
    !formData.username.match(/\^[a-zA-Z]$/);

  if (isInvalid) return true;

  const user = getUserByUsername(username);

  if (user) return true;

  return false;
};

const validateForm = (formData) => {
  if (usernameInvalid(formData.username)) {
    return alert("Invalid username!");
  }
};

We’ve created two functions here. usernameInvalid is the high level validator that will return us a boolean to let us know if the username is invalid. The getUserByUsername function is the additional helper function to see if the user already exists.

This is great because we are following the Open/Closed principle (we don’t have to change anything in the validateForm function to change how a username is validated) and it nets us more cohesive, finely-scoped functions which come with all sorts of benefits.

Conclusion

Abstracting out long conditionals into variables or functions saves developers a lot of eye strain. In some cases where more logic is required to check if a condition is true it’s the bare minimum you need to do in order to follow good software design.

Next time you’re having to check various conditions in a single if-statement, consider abstracting!


Tags

Kodi O'Neil

Kodi O'Neil

Software Developer

Expertise

Social Media

www

Related Posts

Better Code Series Tip #3: Exit early!
Better Code Series Tip #3: Exit early!
December 16, 2022
2 min

Quick Links

About UsContact Us

Social Media