Why not use a Boolean parameter?

Asked

Viewed 1,694 times

28

I’ve seen in some places you shouldn’t wear something like this:

int teste(object obj, bool especial) { ... }

There within the function some operation will be done or not, depending on what you receive in especial.

Why is it bad to use this form? What would be better to do then? Having two functions seems an exaggeration. In some cases it may get confusing to do separate functions, can generate repetition and maybe even break the DRY.

  • No . Net framework (mscorlib) only 1.89% of the functions made available (public) has a bulletin as the last argument. It doesn’t mean anything, but it may be indicative of how common this practice is used.

  • @Brunocosta yes, I think you should use where you fit. But if you analyze new things just I think you tend to use a lot less. In fact there is a lot of case that is abusive.

6 answers

29


Each case is a case.

Every time I see some "don’t do it this way," "this is bad practice," "avoid it," I expect the explanation of the reason right away. If there is no reason why the technique should be avoided, I do with the recommendation what I do with Youtube advertisements: ignore in five seconds.

I will give an example in which receiving a Boolean parameter is valid, practical and desirable. A lot of people here will say that if you have a Boolean parameter, it’s because you’re doing something like:

public function comprarIngressoCinema(bool tresD) {
    if (tresD) {
        foo();
    } else {
        bar();
    }
}

Then the correct one would be to create two methods, comprarIngressoCinema and comprarIngressoCinema3D, end of the story, their code became cleaner and the royal couple lived happily ever after.

When in fact what you normally have is:

public function comprarIngressoCinema(bool tresD) {
    reservarLugares();
    if (tresD) {
        foo();
    } else {
        bar();
    }
    verificarPromocoes();
    pacote = tresD ? especial : normal;
    pagamento(pacote);
    if (tresD) {
        enviarOculosParaSala();
    }
}

In this case, you have two ways to deal with it. You can use the methodology Go Horse, that preaches that you should worry only about the "how" and not worry about things like the "whys":

public function comprarIngressoCinema() {
    reservarLugares();
    bar();
    verificarPromocoes();
    pacote = normal;
    pagamento(pacote);
}

public function comprarIngressoCinema3D() {
    reservarLugares();
    foo();
    verificarPromocoes();
    pacote = tresD;
    pagamento(pacote);
    enviarOculosParaSala();
}

Some will say that the code has gotten better because you have shorter and more readable methods. The problem is that the amount of repeated code is large, and tends to grow over time. Each time you have to maintain there, there will be two methods to treat. And it will get worse if you need to include more cinema options like Imax, for example.

The alternative is to do as Martin Fowler (just a programmer who has more than twenty books published on design patterns and software architecture) suggests in his article Flagargument (in English). It was from this article that I created my example. Your code would look like this:

public function comprarIngressoCinema() {
    comprarIngressoCinema(false /*tresD*/);
}

public function comprarIngressoCinema3D() {
    comprarIngressoCinema(true /*tresD*/)
}

private function comprarIngressoCinema(bool tresD) {
    reservarLugares();
    if (tresD) {
        foo();
    } else {
        bar();
    }
    verificarPromocoes();
    pacote = tresD ? especial : normal;
    pagamento(pacote);
    if (tresD) {
        enviarOculosParaSala();
    }
}

We changed the code presented as public (which can be part of the interfaces of an API) to two methods without parameters, which is actually more readable. But we keep a method that takes a Boolean parameter. Because just like a kitchen utensil, a tool or any other programming technique, this has its usefulness, and whether the effect of its use will be good or bad depends on the skill of the user and not the technique itself.

  • 4

    It was the one I wanted :), if no one answered this, I would answer almost identical.

  • 2

    Martin Fowler always saving :D

6

Why is it bad ?

Because, as some have already mentioned, it nullifies the principle of cohesion, where functions with only one purpose, begin to perform two or more tasks than those originally designed, leaving the code difficult to restructure when necessary, reduces readability (not always), or even duplication of code or responsibility, because wanting or not the code will have before checking the state of the argument, and choose a path, always. An example of this could be the use of a method carrying this parameter in several parts of the code, where it would be checking and authorizing the same functionality over and over again. It’s still useful in some cases.

Personally I have already a few times come across this problem, while making rips, and one in particular, where I was attributing flags a method according to my needs, but in the end it was almost painful to initiate that method, because I could not identify with the flags I defined, and I even had to create a documentation "for a single method", because even if it is only one, one should always know what it does.

int public getA(obj n, bool a, bool b, bool c)

What would be better to do then ?

Using complementary, or simply cohesive, methods make use of enum, or in general, encapsulate.

int teste(object obj, bool especial) { ... }

For

int teste(object obj) { ... }
() testeEspecial(object obj) { ... }

Clearly the method created will have to follow the same operating logic that was attributed to it when one of its states implied something to the method it implemented.

enum Mock {
  mckA,
  mckB;
};

int teste(object obj, Mock tt.mockA) { ... }

Used in this way, it may be too explicit regarding the details of implementing closely related parts, for more external parts of the application, not to mention that code within the method, will also make use of condition structures to know what to do. Other than that, compared to bool more than 2 states or values can be defined in a single Enum and remains viable.

int teste(object obj, bool especial, bool existe, bool grande) { ... }

In simpler cases, the implementation of a flag or argument booleano appears that way:

int teste(object obj, bool especial) { 
    if(especial){
      // mais codigo
    } else {
      // mais codigo
    }
 }

When, create different methods testeEspecial and testeNormal would be much simpler and efficient. In other cases where the use of flags involves the use of various condition structures, as in this reply for example, duplication of code may occur.

Having two functions seems an exaggeration. In some cases it can get confusing to do separate functions, can generate repetition and maybe even break DRY.

As I said before, and it really is what I think, it is not always, so it is relative to the situation.

If an argument of the type is used bool to enable certain functionality in a method that will eventually perform 2 or more functions beyond those originally intended (unimportant), and at the same time declaring this same functionality in another method, it would be creating a duplication of code. The truth is that there will always be a repeating part of the code, the aim is to prevent it from happening whenever possible.

3

Because the code is not very inductive, for example, if someone is going to maintain their code today, how is this person going to know why that Boolean parameter is there ? if true or false what will change ? It would be better to use an enumeration or static constants as long as it has an inductive name. Another option would be to separate this method into 2 named functions that indicate the difference. Also remember to always add comments to your code as a guarantee, but always leave the code intuitive so you don’t need it. And keep code documentation up to date.

//enumeração
public enum OptionsEnum{
    EXECUTE_ALL,
    EXECUTE_PARTIAL;
}
test(obj, OptionsEnum.EXECUTE_ALL); 
test(obj, OptionsEnum.EXECUTE_PARTIAL); 


//constantes estática
public class Options{
    public final static int EXECUTE_ALL = 0;
    public final static int EXECUTE_PARTIAL = 1;
}
test(obj, Options.EXECUTE_ALL);
test(obj, Options.EXECUTE_PARTIAL);

//funções separadas
public testAll(Object obj){... ; testPartial(obj);}
public testPartial(Object obj){ ...}
testAll(obj);
testPartial(obj);

3

The division of this function into smaller functions will produce more cohesion, that is, a function that does only one thing. Within this function will end up having a if to check the condition and make the next necessary action, it would not be easier to divide it to become clearer and reusable?

Look at the code:

int teste(object obj, bool especial) { 
   if(especial) {
      ...
   } else {
      ...
   }
}

In terms of writing it will be almost the same, but the functions are separate, making it easier if you want in the future to perform function teste2() for other operations.

int teste(object obj) { 
   if(condicao) {
      teste2();
   }
}

void teste2() {
   ...
}

Or maybe:

int teste(object obj) { 
   if(teste2()) {
      ...
   }
}

boolean teste2() {
   ...
}

So using a boolean variable may not be the best practice, even if you write a function less, because it won’t always be clearer and more contextualized with the function’s main operation.

3

Using boolean parameter shows that the function does more than one thing.

public static String render(PageData pageData, boolean isSuite) throws Exception {
   return new SetupTeardownIncluder(pageData).render(isSuite);
}

A method call with only one Boolean type parameter is quite confusing:

render(true);

The ideal is to separate into smaller functions making the code more readable:

renderForSuite();
renderForSingleTest()

Reference: Clean Code: A Handbook of Agile Software Craftsmanship

2

It is not possible to generalize and define any parameter boolean is a problem. Whether it is a problem or not, it will vary from the objective, context or even size of the method/class that is receiving this boolean parameter. And to solve it, it will also depend on whether we are talking about a class or method receiving this parameter.

Simpler cases: few parameters

For example, if I want to popular an object Saldo:

new Saldo("1.12", false);

The first parameter is the credit value and the second is a boolean indicating whether the incoming balance is blocked or not.

For a case like this, simple and straightforward, this boolean won’t give you headaches or confuse you in the use of the class.

Now let’s go to the method. Using your example, we would have a call from this method like this:

teste(objeto, false);

Just as with the class, there are few parameters and usually will not create confusion. Of course when we simply see a parameter false, we have no indication of what that means outside the class and method. In these simple cases, a simple variable extraction can help:

 new Saldo("1.12", isSaldoBloqueado);
 teste(objeto, isEspecial);

Some languages also have named Parameters, which helps a lot in understanding the code and replacing this variable extraction technique. Other Ides, such as Java’s Intellij IDEA, by default display the name of the method’s internal boolean variables in its call (simulating a named Parameter), greatly facilitating the understanding of the code.

In simple cases like this, I end up being more practical instead of trying to break in several methods or classes the code just to try to get rid of Boolean. I’m not saying leaving like this is good practice, but I don’t want to be extreme enough to say that this should not exist at all in the code. But, undoubtedly, they are codes that deserve attention.

Let us leave this for the more complex cases explained below.

More complex cases: many parameters

When we have many parameters, the situation changes picture. Imagine the following class instantiation and call methods:

new Saldo("1,23", true, false, false);
teste(objeto, true, dataLimite, true, false);

Although it can extract some variables and improve the situation, the result is not always satisfactory. For having more boolean variables, the chances of getting confused are higher and, especially in the case of the method, we have a beginning of bad Smell: probably this method is doing too many things or the code internally is confused.

How to solve in objects?

In objects, it is usually simpler to solve. You have two options:

  1. Break into more classes, better distributing responsibilities
  2. Using a Pattern as Builder.

That is, for an object initially like this: new Saldo("1,23", true, false, false);, we could refactor to have:

new Saldo("1,23", new Bloqueado(true, dataLimite), new Composto(false, false));

It can also use the presence of the object itself or not to compose the content of the object itself and using different constructors for this. In the example above, we could have two builders for Saldo, one of them accepting the Bloqueado and the other not.

Alternative:

Saldo.builder
    .valor("1,23")
    .bloqueado(true)
    .dataLimite(dataLimite)
    .saldoComposto(false)
    .saldoCompostoBloqueavel(true)
    .build();

I don’t particularly like the Builder if there is the possibility of refactoring to separate the information into more objects. With the Builder, you can easily forget to set one of the above information and break your code without noticing.

Now, in the case of methods, things can get a little complicated.

How to solve in methods?

With the methods, we have more options, but not all look good. Let’s start with an example.

public String enviar (String mensagem, boolean erro) {

   if (!erro) 
      service.enviarSucesso(mensagem);
   else 
      service.enviarErro(mensagem);
}

This method has more than one responsibility. The call of the same would be:

enviar("mensagem", true);

Just want you don’t want to have this boolean. To solve this case, you can pass the responsibility to whoever calls the method decide:

enviarErro("mensagem");
enviarSucesso("mensagem");

In this way, the methods will do only one thing (only responsibility). However, this can result only in condition transfer (change the if class), depending on the code.

Another alternative, more OOP, could be:

Mensagem mensagem = new Sucesso("mensagem");
Mensagem mensagem = new Erro("mensagem");

Including, in case of use of object oriented classes, techniques as Decorator can delete several boolean parameters, as previously demonstrated in the example of Saldo.

  • 1

    Não dá para generalizar e definir que qualquer parâmetro boolean é um problema I ask the question provocatively, but it is good to say it ;). I will still read calmly before voting, but I do not like the idea "here should not be a boolean, but it is simple I will use one anyway". But then show the proper.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.