BlackBox
BlackBox

Reputation: 643

Anti-if purposes: How to check nulls?

I recently heard of the anti-if campaign and the efforts of some OOP gurus to write code without ifs, but using polymorphism instead. I just don't get how that should work, I mean, how it should ALWAYS work.

I already use polymorphism (didn't know about anti-if campaign), so, I was curious about "bad" and "dangerous" ifs and I went to see my code (Java/Swift/Objective-C) to see where I use if most, and it looks like these are the cases:

  1. Check of null values. This is the most common situation where I ever use ifs. If a value could possibly be null, I have to manage it in a correct way. To use it, instead I have to check that it's not null. I don't see how polymorphism could compensate this without ifs.
  2. Check for right values. I'll do an example here: Let's suppose that I have a login/signup application. I want to check that user did actually write a password, or that it's longer than 5 characters. How could it possibly be done without if/switches? Again, it's not about the type but about the value.
  3. (optional) check errors. Optional because it's similar to point 2 about right values. If I get either a value or an error (passed as params) in a block/closure, how can I handle the error object if I just can't check if it's null or isn't?

If you know more about this campaign, please answer in scope of that. I'm asking this to understand their purposes and how effectively it could be done what they say.

So, I know not using ifs at all may not be the smartest idea ever, but just asking if and how it could effectively be done in an OOP program.

Upvotes: 3

Views: 613

Answers (4)

anujm
anujm

Reputation: 309

This is a great question and is something that's asked at every OO bootcamp I've been a part of. To begin with, we need to understand why code with a lot of ifs is 'bad' or 'dangerous':

  • they increase the cyclomatic complexity of the code, making it hard to follow/understand.
  • they make tests more complicated to write. Ensuring that you test each branch flow in the method under test becomes increasingly more difficult with each conditional and makes test setup cumbersome.
  • they could be a sign that your code has not been broken into small enough methods
  • they could be a sign that your methods have not been encapsulated well

However, there is one important thing to remember - ifs cannot(and should not) be eliminated from the code completely. But, we can generally abstract them away using techniques like polymorphism, extracting small behaviours, and encapsulating these behaviours into the appropriate classes.

Now that we know some of the reasons why we should avoid ifs, let's tackle your questions:

  1. Checking for null values: The Null object pattern helps you eliminate null checks from your code(polymorphism FTW). Instead of returning null, you return a Special Case NullObject representation of the expected object. This NullObject has the same interfaces as your actual object and you can safely call any of the object's methods without worrying about a null pointer exception being thrown.

  2. Checking for correctness of values: There are a lot of ways to do this. For example, you could create a separate ValidationRule class for each of your validations and then chain calls to them together when you want to validate your object. Notice that the ifs still remain, but they get abstracted away into the individual ValidationRule implementations. Look up the Command pattern and the Chain Of Responsibility pattern for ideas.

Upvotes: 4

Dave Schweisguth
Dave Schweisguth

Reputation: 37667

You'll never completely get rid of ifs, but you can minimize them.

Regarding null value checks, a method that would otherwise return a null value can return a Null Object instead, an object that doesn't represent a real value but implements some of the same behavior as a real value. Its callers can just call methods on the Null Object instead of checking to see if it's null. There is probably still an if inside the method, but there don't need to be any in the callers.

Regarding correct value checks, the best path is to prevent an object from being instantiated with incorrect attributes. All users of the object can then be confident that they don't have to inspect the object's attributes to use it. Similarly, if an object can have an attribute that is valid or invalid, it can hide that from its users by providing higher-level methods that do the right thing for the current attribute value. Again, there is still a if inside the object, but there don't need to be any in the callers.

Regarding error checks, there are a couple of strategies that are better than returning a possibly null error value that the caller might forget to check. One is raising an exception. Another is to return an object of a type that can hold either a result or an error and provides type-safe, if-free ways to operate on either result when appropriate, like Java's Optional or Haskell's Maybe.

Note also that case statements are just concatenated ifs (in fact I'd have written the code on the campaign's home page with a switch rather than if/else if), and there are also patterns which replace case with polymorphism, such as the Strategy pattern.

Upvotes: 4

Alex P
Alex P

Reputation: 17

Using switch plus SOLID. Other thinks inherited from this.

Upvotes: -1

Alex Planida
Alex Planida

Reputation: 59

It's better to use if to check the null instead of raising an exception. Also in common cases checking for null helps us to prevent operations with non-initialized variables.

Upvotes: -1

Related Questions