3

I came across this issue at my second job interview. The technical interviewer said multiple times that booleans are not ok to be passed as parameters in methods, rather find another constructs (Enums) to get rid of booleans.

What's the general opinion on this topic?

5
  • You should reformulate the question to tone down the opinion part. Some people here hate that word. Feb 28, 2018 at 16:09
  • 1
    Booleans are semantically the same as an enum which has the values true and false, so you can't blindly say that one is better than the other. Just like pretty much everything else in software engineering, there's also no definite answer on this subject. It may be okay, maybe you should use an enum instead, maybe you should replace the single method taking a boolean by two methods taking no parameters, maybe you should use subtyping polymorphism, etc. Feb 28, 2018 at 16:11
  • 1
    While the use of booleans as parameters often, but far from always, hide other problems in logic, the technical interviewer is at best generalizing. This question may be too opinion based as CodesInChaos hints at.
    – Bent
    Feb 28, 2018 at 16:11
  • @CodesInChaos I don't think there is anything wrong with opinions.
    – user251748
    Feb 28, 2018 at 17:38
  • This topic is debatable, that's why I intentionally had that in the question. Mar 1, 2018 at 9:03

5 Answers 5

14

Enh. It's generally distasteful to pass in a boolean that governs behavior - "If X do this other stuff". The function then tends to not have a single focused responsibility making it harder to test, harder to reuse, harder to maintain, etc.

And it's generally distasteful in most languages to just have a boolean parameter because at the call site you just see function("abc", true);. What the hell does true mean here? Some people advocate for enums (or static constants) instead. Some people advocate for separate functions instead. This is of course made worse when there's a bunch of booleans and it's hard to remember the order. Languages that allow function("abc", recursive: true) style labelled parameters can mitigate that a bit.

But frankly, there's a lot of use cases where boolean parameters are simple and clear. And the impact of using boolean parameters is so mild that the dogmatic "thou shalt not have bool parameters" seems silly.

4
  • 1
    If refactoring into separate functions, the simplest way is usually to keep the boolean-taking method, but mark it as private and have the public methods call it with the requisite parameter. This avoids having to repeat yourself or a more major refactor, but provides a more readable public API to your class. Feb 28, 2018 at 16:34
  • 3
    true, true, true
    – user251748
    Feb 28, 2018 at 17:36
  • @MaciejStachowski good point Mar 1, 2018 at 9:05
  • 1
    Use Objective-C, or use Swift. I remember using a framework that initialised a Point with a constructor having two int parameters (x, y). Then they decided to change this to (v, h) = vertical, horizontal so all constructors still compiled but had reversed their meaning.
    – gnasher729
    Mar 1, 2018 at 20:22
7

In a code-driven world, having explicit methods that do things is preferred:

class ControlDerivedUIClass
{ 
   void enable(); 
   void disable(); 
} 

However, in a data-driven world, say when you're reading settings from an external data store, like a file, then it might make more sense to allow your objects to be more data driven, as in:

class ControlDerivedUIClass
{ 
   void enable( bool value ); 
} 
1
  • The boolean problem almost inevitable results in if/else statements at some point. The tricky part (as you describe) is where to offload that responsibility. Mar 2, 2018 at 5:38
5

You could have this:

Squirt squirt = new Squirt(true);                         // WTF does "true" mean?

Or you could have this:

Squirt squirt = new Squirt(WITH_AGGRESSIVE_FROBINATION);  // Ah! I get it!

But there is a third way:

interface Frobinator { ... }

Frobinator f = new AggressiveFrobinator();
Squirt squirt = new Squirt(f);

This is an example of delegation. The f object becomes the delegate of the squirt object. When the squirt needs to do frobination, it calls on the f object to do the actual work.

Delegating a responsibility like this probably is an over-engineered solution for some problems. It forces the caller to write more code, But in the end, it is the most flexible, powerful solution.

  1. It gives you a way to extend the Squirt class without changing its code. You can define and test new ways of frobinating without touching any old code lines. I.e., the Squirt class can be open for extension, but closed for modification.

  2. The callers potentially can define their own custom ways of frobinating.

  3. It makes your Squirt class more testable: You no longer need to separately test what squirt.x() does with aggressive frobination, and test what squirt.x() does with normal frobination, AND test what squirt.x() does with some new kind of frobination... Now you can replace all of those with just one test that makes sure that squirt.x() interacts correctly with its frobinator. (Note, this basically is the central idea of unit testing.)

1
  • I will tend to decouple a responsibility into a delegate/interface or decoupled input once I reach 3 code paths or more, or the function is predicted to grow or is sufficiently complex by default. Obviously it depends on the circumstances too. Mar 2, 2018 at 5:36
3

I would think this applies to ints and strings as well as booleans. These are all primitive data types in the domain of programming languages.

When you pass a primitive type as a parameter, as the reader of that code you don't know what the meaning of true vs. false might be, unless you're using a language that offers parameter assignment, as in foo ( a, b, optionParameter := true ). You have to look up the methods signature to see the name of the parameter to get some meaning.

Generally speaking, we would generally rather see problem domain-oriented abstractions and types. We get these by using enums, classes & structs, etc: giving them meaningful names and operations. With those the code is more self-documenting.

2
  • All code should be self-describing.
    – user251748
    Feb 28, 2018 at 17:38
  • optionParameter := true doesn't really tell me what's going on either.
    – gnasher729
    Mar 1, 2018 at 20:23
1

First, this has nothing to do at all with object-oriented programming. You have exactly the same problem with plain old function calls, and the problem is that if a parameter is just "true" or "false", then the reader of the code has no chance to figure out what the code does, except if it is the only parameter of a suitably named function: object->setVisible(true) is absolutely fine.

There are languages where parameters are named in a call. For example, object.setOptions (visible: true, opaque: false, animated: true) is again fine. But object.setOptions(true, false, true) is a big problem, because just reading that line of code I have no idea what is happening. I don't know what the parameters are, and definitely no idea of their order. In this case, using an enum and calling object.setOptions(eVisible, eTransparent, eAnimated) is fine again. Enums, not constants set to boolean values.

What's worse is that not only do I not know what your code means, the compiler can also not help you if you get it wrong. object.setOptions(false, true, true) will compile just fine and give completely wrong results. object.setOptions(eTransparent, eVisible, eAnimated) will hopefully not compile, so you can immediately fix your problem.

Not the answer you're looking for? Browse other questions tagged or ask your own question.