0

I know there are some questions about boolean flags: Is it wrong to use a boolean parameter to determine behavior?, Multiple boolean arguments - why is it bad? which indicates the following code is bad:

public void myMethod(boolean b){
    doThis();
    if(b){
        doThat();
    }
}
myMethod(true);

and should be refactored as:

public void myFalseMethod(){
    doThis();
}
public void myTrueMethod(){
    doThis();
    doThat();
}
myTrueMethod();

which eliminates the if-else with that boolean. However, the solutions seems works for hardcoded boolean parameters only, what if the original boolean value is not hardcoded, but loaded from some dynamic load data? eg:

boolean b=this.userData.isValid && this.checkbox.selected;
myMethod(b);

I found it is almost unavoidable to "use boolean to determine behavior", what I tried to refactor:

Refactor method 1: separate 2 functions:

boolean b=this.userData.isValid && this.checkbox.selected;
if(b){
    myTrueMethod();
}else{
    myFalseMethod();
}

Which I think it is not better than the boolean flag version because it is still "use boolean to determine behaviour". Also it requires me to repeat the if-else if there are other places that call myMethod(b), which seems violating DRY principle.

Refactor method 2 : introduce parameter object:

public class MyMethodParameterObject{
  public boolean b;
}
public void myMethod(MyMethodParameterObject obj){
    doThis();
    if(obj.b){
        doThat();
    }
}
MyMethodParameterObject obj=new MyMethodParameterObject();
obj.b=this.userData.isValid && this.checkbox.selected;
myMethod(obj);

I don't understand why it would be better than the original one because the if-else still exists, but have more codes, seems more complex.

Refactor 3 : Polymorphism

public interface IMyClass(){
    public void run();
}

public class MyFalseClass implements IMyClass{
    public void run(){
      doThis();
    }
}
public class MyTrueClass implements IMyClass{
    public void run(){
      doThis();
      doThat();
    }
}
boolean b=this.userData.isValid && this.checkbox.selected;
IMyClass myClass=null;
if(b){
  myClass=new MyTrueClass();
}else{
  myClass=new MyFalseClass();
}
myClass.run();

Polymorphism makes the good uses of virtual table, which selected the required implementation automatically. But in this case, I still need a if-else to convert the boolean value to corresponding implementation, which I think it is more complex and unnecessary, why don't I use boolean flag directly?

Refactor 4 : use reflection:

public class MyClass{
    public void myMethod_false(){
        doThis();
    }
    public void myMethod_true(){
        doThis();
        doThat();
    }
}
boolean b=this.userData.isValid && this.checkbox.selected;
Method m=null;
try{
  method=myClass.getClass.getMethod("myMethod_"+b);
  .
  .
  .
}catch(Exception e){
}

It truly eliminates the if else, avoiding "use boolean to determine behaviour", but I think it is harder to maintain, besides having more code, I may type the method name wrongly in the string. Also it is harder to find which method is actually called.

So I think if the boolean is from dynamic data, using boolean flag is the simplest solution. Is it true? if not, what am I missed about "refactoring boolean parameter"? Is there any solutions to refactor "dynamic boolean parameter" in better way?

4
  • 3
    There is nothing wrong wit Boolean parameters in a good language. The problem is languages like C++ where you could have a call “myfunction(false, true, false)” and have no idea what true and false means. “Myfunction (filtered: false, update: true, redrawUI: false)” makes clear to the caller what each bool does.
    – gnasher729
    Commented Oct 20, 2023 at 7:23
  • 1
    @wcminipgasker2023 I don't like that you're on a path to obfuscating that doThis() will always be executed.
    – Pieter B
    Commented Oct 20, 2023 at 7:26
  • 1
    And sometimes “the cure is worse than the disease”. The reflection method is ingenious and horrifyingly bad.
    – gnasher729
    Commented Oct 20, 2023 at 7:26
  • The linked question as well this one is about separating mutually exclusive tasks that depend on a boolean parameter. There can be boolean parameters that are not so, like configuration flags etc.
    – S.D.
    Commented Oct 20, 2023 at 8:05

3 Answers 3

1

All your alternatives have the same if-else logic somewhere, so I see little reason not to just put that logic inside the method that actually needs it.

You could just provide all alternatives:

myMethod(boolean b);
myTrueMethod() => myMethod(true);
myFalseMethod() => myMethod(false);

If you know what to call statically you can use one of the named methods, otherwise you can provide the parameters. The same approach can also be used for many other types of parameters.

This may also be the simplest to implement, since it is fairly common to check the boolean at in the middle of the method, and splitting the method into three parts might just add unnecessary complexity.

I would recommend being fairly pragmatic when it comes to rules about coding. It is very useful to know about such rules, but you also need to know the actual problem they are trying to solve and in what context to apply them. Dogmatically applying rules without the underlying understanding will not turn out well.

1

So I think if the boolean is from dynamic data, using boolean flag is the simplest solution.

As with everything else in Programming, there are no absolute "right" and "wrong" answers.
You need to decide what works best in your application.

Yes, it is generally accepted that methods with Boolean arguments are "bad" because, unless the method is very, very clearly named, the meaning of the argument can be obscure. For example:

Dissimulate( Boolean )  // Huh?
DrawLine( int, int, int, int, Boolean )   // What does that do, again? 

Both of these will have you reaching for the Documentation to find the meaning of that Boolean argument and that's Bad! ;-)

In other cases, though, where it's clear and obvious what the argument does, there's no harm in using them, if you need them.

Enable( Boolean ) 

Also, Object Orientation is (or, at least, was) all about encapsulating Data and Logic together and you could argue that calling, say, Enable() or Disable() consistently for some given, Boolean value is a job for the class itself and so the parameterised method should appear as a class method, rather than having [all your] external agents trying to work out when to call which method.

  • Code the 'if' construct once in the parameterised method and reuse it all over the place, or
  • Only provide the "authoritative" methods and have those 'if' constructs scattered all over the codebase?
    YMMV.

Side Note:
Of course, whatever is calling those parameterised methods will, almost certainly, be mapping those Boolean, input values from some other data value(s). How do you persist a Boolean value into, say, a configuration file? You certainly can't put the binary representation of a Boolean value into a file (well; OK, you can, with some hacky, binary Serialisation mechanism ... but [please!] don't). You have to use '0' & '1' or 'N' & 'Y' or 'F' & 'T' or some other character representation and the code reading those values will have to do that mapping. (To do otherwise is putting [file] persistence logic inside your method, where it doesn't belong.)

1
  • I’ve been writing a bit of C++ recently and ran into the problem that function overloading only works if parameters have different types. So you can’t have two functions with same name and an int parameter with completely different meaning.
    – gnasher729
    Commented Oct 20, 2023 at 19:21
0

Were are talking about the art of creating a good API. That means to create the domain-specific language that calling code is to use for its problem solving.

A language should be precise, clear, readable and avoid redundancy.

  • Seeing a call like myMethod(true) will rarely be readable (depending on the method name), only in cases like setEnabled(true).
  • When deciding between myMethod(boolean), or myMethod_true() plus myMethod_false(), that depends on the purpose of the method. Do the true and the false version of the call fulfill tasks that deserve a common name (then having the decision as a parameter is a good thing), or are the tasks different (then having different names is better)? So, for clarity, if it's one task with just a minor option, use a parameter, otherwise use two different methods.
  • Having all of myMethod(boolean), myMethod_true(), and myMethod_false() is highly redundant, and leaves you with the problem of introducing three different method names that also convey their common root.
  • If a parameter is the way to go, it need not be a boolean, a two-valued enum can do the job as well, and you can give the values better names than true or false, e.g. Enabled and Disabled. Use names that make sense from a caller's perspective.
  • Sometimes you'll find code where a no-parameter overload like myMethod() exists, effectively defaulting the flag to some hidden value. This often harms clarity as it gives you no clue whether the true or the false version is chosen. This is only justified if one of the versions clearly stands out as the default one, and the other is only meant for special, optional circumstances - which also implies that a boolean is not a good choice for that parameter.

So, when you think about your APIs from the caller's view, you'll rarely end up with a boolean parameter. More often, you'll introduce a specific enum or create two different methods.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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