Why in some if’s situations are considered bad?

Asked

Viewed 5,453 times

74

I read in some places that it is not recommended the exaggerated use of if, because it makes your code difficult to read and maintain, so it’s not a good practice.

What would be the overuse of if in a code? What are the best practices in a code to avoid many ifs?

EDIT

The idea of anti if campaign is not actually exterminating the if, It does encourage the correct use of object-oriented programming, which makes code cleaner and easier to maintain. My question is actually how should I replace the zillions of if s by a good class diagramming. What object-oriented programming concepts are used to achieve such a goal?

Among other words: Why exchange the imperative conditional, by polymorphism or other way that facilitates the understanding and organization of the code?

Source(s) of motivation / reading:

Anti if campaign

Anti if Campaign(English)

  • It is the general idea of exchanging ifs for a hierarchy of classes. There will be cases where it is worthwhile and cases where it is not worthwhile. Even with OO o if will always exist, because somewhere you will have to choose what type of instantiating object.

  • Yeah, it’s not an anti-if campaign, but a campaign use OO correctly...

  • there is a English version of the anti if campaign (or ours is a version of theirs?) which explains with a wealth of details the reason of the campaign. It would be nice to have these explanations and examples in Portuguese.

  • @Math I didn’t like the campaign site (in English). It doesn’t have an objective and easy to find justification text! Or you have a direct link? I didn’t find.

  • @bfavaretto the part that justifies is the tab Get Started. I at least liked it, maybe by having self-identified very quickly, rs..

  • 1

    The excess of If’s in addition to increasing the complexity of the code, increases the difficulty in understanding and when making future maintenance. If you are using concepts such as Object Orientation, there are ways to supply an eventual chain of If’s. Take a look at this article here that I think it’s pretty cool. It’s been a long time in my favorites. : ) http://blog.caelum.com.br/como-nao-aprenderorientacao-objetos-o-excesso-de-ifs/

  • I think it’s more a matter of subjective and personal taste. In some cases polymorphism is preferred (when this different behavior is actually something intellectually differentiable in terms of being a distinct class), but this is not the general rule. For example: the person class: there are people who like rock, others like samba, etc. Is it right to create a class for each different behavior? A more appropriate question: "How to route code efficiently"

Show 2 more comments

8 answers

56


I begin by quoting a comment given in original English OS question:

if statements are evil in the way Hammers are evil. Some people Might misuse them, but they’re an Essential tool. - Dominic Rodger Oct 12 '09 at 12:08

In free translation: "IF commands are as demonic as hammers. Some people misuse them, but they are essential tools."

This comment makes sense in that without a flow control structure it is simply not possible to build a computer program that is useful, after all the troubleshooting depends on decisions in some scope.

Although Ifs are really fundamental elements in any programming language, the lines of argument that classify IF commands as bad - like the Anti-if campaign cited in the question - are not as dogmatic as they seem. As indicated on the website of the Anti-if Campaign, its motto is this:

The Goal of the Anti-if Campaign is to raise Awareness of effective use of software design Principles and Practices, first of all removing bad, Dangerous Ifs.

In free translation: "The goal of the Anti-if campaign is to sensitize developers about the effective use of software design principles and practices, in particular the removal of bad or unsafe Ifs."

Despite using a bold marketing strategy (by saying that IF is demonic - or evil in the original in English), what these campaigns propose is to be careful with the use of conditionals in situations where they may be being misused or even dangerous in order to hinder the maintenance of the code.

The classic example is based on a single class (the original source of that code deserves a +1 just for being plainly inspired in jokes of the British humor group Monty Python!):

inserir a descrição da imagem aqui

public class Passaro {

    private double m_dVelocidadeBase;
    private double m_dFatorPeso;
    private int m_iNumCocos;
    private boolean m_bPregado;
    private double m_dVoltagemChoque;
    private TipoPassaro m_eTipo;

    . . .

    double getVelocidade() {
        switch (m_eTipo) {
            case TipoPassaro.ANDORINHA_EUROPEIA:
                return m_dVelocidadeBase;
            case TipoPassaro.ANDORINHA_AFRICANA:
                return m_dVelocidadeBase - (m_dFatorPeso * m_iNumCocos);
            case TipoPassaro.PAPAGAIO_AZUL_NORUEGUES:
                return m_bPregado ? 0 : m_dVelocidadeBase * m_dVoltagemChoque;
        }
        throw new RuntimeException ("Oops! Essa parte do código não deveria ser executada.");
    }

    . . .

}

In this example, the class Passaro can represent different types of bird instances (among them, the European and African swallows and the Norwegian Blue parrot), and therefore the method getVelocidade has a great complexity related to the different ways in which the speed of the bird is calculated with respect to its type. This complexity is expressed in the need to have a number of decisions (several if or also a switch) with respect to the type of bird. If other methods exist in that class (such as, getPiado, getFome, etc.), most likely they will also have Ifs to deal with the differences, and the complexity of the solution and maintenance difficulty will only increase (because if a new type of bird needs to be added, all the places/methods with Ifs will need to be changed).

The suggested way around the problem is due to the appropriate use of the concepts of inheritance, abstraction, and polymorphism of Object Orientation. Instead of having a single class Passaro and instantiate it for each different type of bird, it can become an abstract class (i.e., that cannot/should not be instantiated) or an interface, so that it contains the properties and signatures of standard methods among the different types of birds. You can then create new classes, one for each type of bird itself (classes AndorinhaEuropeia, AndorinhaAfricana and PapagaioAzulNoruegues, in the example), so that they inherit from the base class Passaro and implement the specificities of each type in their own superscript methods.

The result (also based on the original source mentioned above) would be as follows:

inserir a descrição da imagem aqui

public abstract class Passaro {

    private double m_dVelocidadeBase;

    . . .

    double getVelocidade() {
        return m_dVelocidadeBase;
    }

    . . .

}

public class AndorinhaEuropeia extends Passaro {

    . . .

    // Apenas um exemplo, pois é desnecessária a reimplementação nesse caso, 
    // uma vez que ela não altera o método original da classe pai.
    double getVelocidade() {
        return super.getVelocidade();
    }

    . . .

}

public class AndorinhaAfricana extends Passaro {

    private double m_dFatorPeso;
    private int m_iNumCocos;

    . . .

    double getVelocidade() {
        return super.getVelocidade() - (m_dFatorPeso * m_iNumCocos);
    }

    . . .

}

public class PapagaioAzulNoruegues extends Passaro {

    private boolean m_bPregado;
    private double m_dVoltagemChoque;

    . . .

    double getVelocidade() {
        return m_bPregado ? 0 : super.getVelocidade() * m_dVoltagemChoque;
    }

    . . .

}

Thus, Ifs are not needed because each specific class (inherited from Passaro) implements its own way of calculating speed. In addition, object orientation polymorphism allows you to treat birds in a generalized way, so you can deal with the differences without having to make explicit Ifs:

Passaro oUmPassaro = new AndorinhaAfricana();
Passaro oOutroPassaro = new PapagaioAzulNoruegues();
Passaro oPassaroQualquer = fabricanteDePassaros.criaUmPassaro(); // Método independente de criação

System.out.println(oUmPassaro.getVelocidade());
System.out.println(oOutroPassaro.getVelocidade());
System.out.println(oPassaroQualquer.getVelocidade());

If we compare the two approaches considering a possible future need to add a new type of bird (say, the Tucano), which has its own ways of calculating speed (beak size maybe influences?), we will note that in the first approach it will be necessary to add a new type to the enumeration, and it will be mainly necessary to add a new case (ie a new IF) to the method getVelocidade class Passaro. In the second approach, the class Passaro does not need to be changed, just create a new class Tucano and inherit it from Passaro.

This distinction not only influences the amount of code to be written (and here one could argue that adding an IF is much easier - and uses fewer lines of code - than creating a whole new class) and the ease of maintenance (again, one could argue that it is easier to locate where to change if all the code is in one class), but on other important issues. For example, in languages such as C++, recompiling the code after a change in a class used in multiple locations causes a longer build time (as more dependencies are recompiled). If approach 2 is used, the files that depend on the other classes (other than the Tucano) do not need to be recompiled. It should be clear that this has an impact also on the tests already performed. The more localized a change is, the less chance it has of causing defects (bugs) regression.

Note also that this example is very specific. In this scenario, the differences in the speed calculation are due to distinct behaviors between objects, which in itself already indicates that it makes a lot of sense to build a class hierarchy. This means that in addition to the benefits argued the resulting code is potentially a simpler and clearer understanding.

So by no means does that mean that the IF command is evil. Only that its use in examples like this does not seem to be the most indicated way of solving the problem, especially in object-oriented languages. Surely one can provide examples where using a sequence of Ifs is more straightforward and simple than building an entire class structure. What occurs to me right now is an example of displaying error messages:

public void displayErrorMessage(Error e) {
    if(e.Type() == IOError.FILE_IS_READ_ONLY) {
        // Mensagem de aviso, solicitando fechar outras aplicações e tentar novamente
    }
    else if(e.Type() == IOError.FILE_EXISTS) {
        // Mensagem de aviso importante, solicitando anuência
    }
    else if(e.Type() == IOError.ERROR_WRITING_DATA) {
        // Mensagem crítica, informando impossibilidade de gravação
    }
    . . .
}

Of course it would be possible to construct this same decision through a hierarchy of classes, but this does not seem really necessary in this example, especially if the class Error is native to language. Although the class is defined by the programmer himself (in which one could inherit the various types of errors and specialize a getErrorMessage method in a similar way to the one previously exemplified), the questions that fit here are: How many new IO error messages could appear in the future? there is indeed some possibility of change where Ifs will actually harm the understanding or maintenance of that code?

In closing, I’d like to quote this other example where the campaign is to avoid the use of Fors. In the example cited, it is proposed to exchange this code:

public class Department {
    private List<Resource> resources = new ArrayList<Resource>();

    public void addResource(Resource resource) {
        this.resources.add(resource);
    }

    public void printSlips() {

        for (Resource resource : resources) { 
            if(resource.lastContract().deadline().after(new Date())) { 
                System.out.println(resource.name()); 
                System.out.println(resource.salary());
            }
        }
    }
}

for that:

public class Department {
    private List<Resource> resources = new ArrayList<Resource>();

    public void addResource(Resource resource) {
        this.resources.add(resource);
    }

    public void printSlips() {
        new ResourceOrderedCollection(this.resources).select(new InForcePredicate()).forEachDo(new PrintSlip());
    }
}

with the creation of three classes and two interfaces. The problem pointed out is that originally the method printSlips does more than it should, because it iterates between resources (the for), selects which resources should be printed (the if) and has the responsibility to print the component items of each resource (the various System.out.println). The solution creates a class to contain and iterate over the resources, one to define the selection and one to print resources.

This alternative is nice, but there are others. For example, the class itself Resource could have a method toString which already prints what should be printed, and the method printSlips could receive as parameter a class instance for checking the print conditions (i.e., a filter).

Anyway, which is the best one? Read the original post and draw your own conclusions. Again, this does not mean that the FOR is evil, demonic or bad. :)

  • 6

    I think even you have learned or at least consolidated something better about it :) In these moments we can show objectively (or almost) how something should be used. Of course, there can always be controversy. But we don’t need absolute answers, we need something to teach how things work, how they should be thought through. Even if someone disagrees, the answer is very good. And I’ll repeat something here that I’m proud of: we have a much better answer here than in the original OS :)

  • 1

    In fact. Thanks for the ear tug. :)

  • 3

    I was too lazy to read everything, but +1 for finding an example that cites Monty Python other than Python. After all, "A king needs to know these things".

19

Look, there’s no kind of Rule or Pattern to these things, but stopping to think, you can identify the best method to program, as @Igorcarvalho said, this is "feeling" IE, it’s like a feeling, that would be this such thinking, which would be the best method of doing in programmatic vision, aiming at performance, aiming at the understanding of the code and aiming at few lines, objective solutions. But there are some basic concepts that you can pick up that are as follows:

Examples

Suppose you don’t have a month-long function translated into your programming language, for example in javascript (my most commonly used language):

This case would be using the if(the month in javascript is zero-indexed begins from 0):

var mes = 1;
if (mes == 0)
  var strMes = "Janeiro";
if (mes == 1)
  var strMes = "Fevereiro";
if (mes == 2)
  var strMes = "Março";
if (mes == 3)
  var strMes = "Abril";
if (mes == 4)
  var strMes = "Mario";
if (mes == 5)
  var strMes = "Junho";
if (mes == 6)
  var strMes = "Julho";
if (mes == 7)
  var strMes = "Agosto";
if (mes == 8)
  var strMes = "Setembro";
if (mes == 9)
  var strMes = "Outubro";
if (mes == 10)
  var strMes = "Novembro";
if (mes == 11)
  var strMes = "Dezembro";

Instead of going through all this trouble Why not do the following:

var aryMes = ["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"];
var mes = 1; //1 é fevereiro no date().getMonth() do javascript.
var strMes = aryMes[mes];

You know, it’s much better, it’s much smaller, and it saves a lot of lines of code, but that you have to learn over time, according to how much time you program, and at what intensity, you’ll see different solutions to problems that you’ll say:

"I don’t believe I could have done in a row what I did in 25"

or else:

"God, I had no idea there was this function that did everything for me"

Basically, you have to learn over time, but to make this example I thought of one thing:

"There are 12 different values, when there are many values, I must use one array, for if and switch(case) would give me many lines of code."

This may be a good thought, but each programmer has his own methods of doing, a lot of things are logical, like it’s in the face, like the example I gave you, for me it was meant to do this, but for you it may not be.

The tip I give you is to continue programming, learn, learn more, see new solutions to things you do as routine, perfect your code and learn constantly.

Directly answering the question’s title:

Because in some if’s situations are considered bad?

Because they can become difficult to understand, in the eyes of another programmer, or even you after a while, also because they depend on the way they were used, but reduce the performance of your algorithm, can consume much more lines of code than usual, so you have to be careful to know where to use, because a condition should only be used when you have no other option, because the function of if is only for condition, if you can find a way out not to use a if and not having to write more code so you did well. Because there are cases that the use of if is useless as for example:

var saldo  = 50;
var divida = 50;
if ((saldo - divida) <= 0)
  alert('Desculpe, Seu saldo acabou.');
else
  alert('Seu saldo está em:'+(saldo - divida)+' reais');

In this case you could have done so:

var saldo  = 50;
var divida = 50;
alert('Você tem um saldo de:'+(saldo - divida)+' reais);

Note that you didn’t need the if, if you think, you might find ways not to use it but of course, there are cases that if will be great, but there will be some that will be unnecessary or even bad, if they are reason for performance reduction, or increased lines of code or unnecessary programming or difficult to understand.

Some quotes:

"It’s no use being smart without wisdom."

"The lazier you are, the better your problem solutions will be."

"Better think and do well done than do fast and have to redo 5 times."

Well... in short, good luck on your journey, programmer !

"May the Force be with you - Master Yoda"

  • 6

    The problem with arguing with "common sense" is that everyone has their own. That is, to say to use common sense, is more or less to say "use what you think best". What we need is objective criteria. Parts of your answer say this. But whenever I see "use common sense" I read "do what you think best". After all common sense is just that, an opinion, honest, true, than the person stifle.

  • You’re right about that, but the Common Sense I meant was not for this sense, it was for the sense that is really better and not personal of each, you know some other word that would fit better?

  • 1

    No, I can only replace this with "do what you think best" or "use your own judgment to decide". But none of them is a clear criterion.

  • 2

    Feeling It’s important and it comes with the experience, but even experienced programmers can be proud and think they do it "in the best possible way," when they may actually be putting good ideas aside for the sake of their personal experiences alone. There may be objective criteria for refactoring code. For example, even though I already find my code "beautiful" (according to personal criteria), at the moment I am reading this material, who even talks about if’s (Replace Conditional with Polymorphism) and he’s opening my eyes to many things.

  • 1

    estou lendo este material, que inclusive fala sobre if's (Replace Conditional with Polymorphism) e ele está me abrindo os olhos para muitas coisas. Man, I was so happy to read that!

  • @bigown, actually, I was asking you if you know any words that demonstrate "Do it better, not better for you, but better on programming issues"

  • And I said I know nothing better. I see no problem in the expression, but in the intention of it. to have common sense is common sense. Everyone thinks it’s good to have common sense. But saying "have common sense" doesn’t mean anything that someone doesn’t know. Saying what common sense would be in the specific case would help more. Even if someone disagrees. Although putting opinions here is not ideal, saying "follow your opinion" is even less ideal. In parts you showed something useful, although I think that replace a if by a lookup is not exactly what the question is talking about. We already have 2 very interesting answers.

  • @bigown , I edited my question, now I explained myself better at the beginning. But the question is asking why using if is bad, I said it is bad because it consumes more lines of code, because it becomes less efficient in terms of performance, and also because it is difficult to understand.

  • @Math Yeah, but you gotta watch out for "refactor Syndrome". :)

  • 4

    @Pauloroberto I think your answer is cool because it presents a comparison in which there is actually an objective difference (performance) when using or not Ifs: making a sequence (in the worst case, December) of 12 comparisons probably takes more time than addressing a position in a matrix (whether that significant difference or not). But what the others have pointed out is that beyond this argument, others like "... it’s much better, it’s much smaller and saves many lines of code", are merely opinionated. Less/more lines do not necessarily imply quality/defect or clarity/cloudiness.

  • 1

    It’s certainly a valid answer to the question "Why in some ifs situations are considered bad?" I just said some of them :)

  • 1

    @Pauloroberto Yes, I also think valid. :)

  • 1

    I was just going to say that switch is not so much better than if, but this answer showed the elegant construction with the list -- which with some effort can also be used in C and C++.

  • "Quanto mais preguiçoso você é, melhores vão ser suas soluções para problemas.", what is the source @Pauloroberto? I will use in my daily life. Hehe

  • If we could give a name to that if of the months, we could call it if from hell

  • 1

    +1 by Master Yoda

  • Thank you, my dear padawan, may the force be with you.

Show 12 more comments

18

Conditionals are one of the largest sources of complexity for code. A 1 conditional code has two paths that the execution stream can take; a 2 conditional code in sequence has 4 possibilities and so on. However, your program will need some conditional logic to do something useful which indicates that the best way to ask the question is not "because ifs are bad" but rather "how to make my conditionals the best way".

There are 2 points that I will discuss. The first is the boolean blindness and the second is "Expression problem" (I don’t know if you have a Portuguese name for it):

One problem you’ll have with ifs is that they can only make binary decisions about boolean expressions. If your model has more than two cases to deal with then there will be a loss of information if you use booleans to represent your data. For example, suppose I have a program with 3 possible states: "Off", "On" and "On". If we use an Enum to represent the program state and a switch to interpret the state, we can treat the three cases cleanly and also get a compiler warning if we forget to treat one of the cases (or if one of the cases is redundant):

switch(estado){
   case DESLIGADO: print("A"); break;
   case LIGANDO: print("B"); break;
   case ATIVANDO: print("C"); break;
}
// se um dia eu adicionar um quarto estado na definição da enum,
// um bom compilador me avisa que é preciso atualizar essa parte do código

Using ifs on Enum you can write something very similar, despite missing some of the analysis at compile time.

if(estado == DESLIGADO){
  print("A");
}else if(estado == LIGANDO){
  print("B");
}else if{estado == ATIVANDO){
  print("C");
}else{
  error();
  // Se eu adicionar um caso novo, vai dar um erro só em tempo de execuçao
  // mas é melhor que nada, né...
}

The biggest problem is if you use booleans to encode your state instead of an Enum.

bool desligado;
bool ativado;

// desligado=1 ativado=0 ==> DESLIGADO
// desligado=1 ativado=1 ==> ????
// desligado=0 ativado=0 ==> LIGANDO
// desligado=1 ativado=1 ==> ATIVADO

if(ativado){
  print("C");
}else if(desligado){
  print("A");
}else{
  print("B");
}

That code is boring now. In addition to the compiler not being able to help you if you decide to change the number of states of the model, we now have 4 possible combinations of values for the flags but only 3 valid possibilities. This means that the program can "swallow errors": for example, if you set enabled=true and off=true your program will consider it as if it was ON instead of giving an error as it should. Ultimately, the real problem here was using a combination of flags instead of enums - ifs are just a symptom of that.


Now I go to the second point, which is what the anti-if campaign raises.

Suppose you have modeled a system in which there are several cases for your data, various actions you can do with the values and each case-action combination will do something different. How to program this? There are two basic forms:

The first is using a switch within each function:

//pseudocódigo

function f(val){
  switch(val.caso){
    case A: print("F A"); break;
    vase B: print("F B"); break;
  }
}

function g(val){
  switch(val.caso){
    case A: print("G A"); break;
    case B: print("G B"); break;
  }
} 

The second is to use dynamic method dispatching or callbacks:

//pseudocódigo

class A:
  method f(): print("F A");
  method g(): print("G A");

class B:
  method f(): print("F B");
  method g(): print("G B");

Which of these two ways is better? It depends on how your program will evolve in the future! Sometimes it will be better to use classes instead of ifs but sometimes using ifs (or switch) is better.

  • In the version with switch is easy to add a new function without having to touch the existing ones, but it is difficult to add a new case to enumeration, since you will have to tinker with Switchs of all functions.
  • In the version with classes is easy to create a new case (just create a new class) but it is difficult to add a new method because you will have to tamper with the implementation of all existing classes.

One comment on the side: I think one of the reasons why so many people are encouraging the use of methods instead of switch is that in most programming languages "Switchs" are weaker than so-called methods. In functional programming languages such as Haskell or Ocaml, enums can store additional data (in addition to the tag determining what the case is) and there is a more powerful Pattern matching syntax than the traditional switch (makes an automatic "unpacking" of the fields depending on which case you are dealing with and do not need to keep giving "break" at the end of each case)

  • That’s a very good answer.

  • +1 for having explained what is and what are the dangers of Cyclomatic Complexity (despite not having used this term)! :-P

12

I read all the answers, and actually the question has already been answered.

However, although the question makes it clear that its interest is from the point of view of object orientation, I believe it is relevant to add to the topic another reason to fear constructions if, that is related to the internal architecture of some processors.

If we take into account an Intel compatible computer (x86, x64), the if is that part of the service carried out by the pipeline processor is lost, once the conditional jump to another address will cause the already decoded instructions to be discarded.

For example, in the code below, some instructions from Bloco B can be decoded by the processor, while the result of the if is not ready. When the result is ready, if the answer to the question is false, the processor should make a jump to the else, effectively executing the Bloco C, whose instructions were not in his pipeline. Thus, the work already performed to decode the instructions of Bloco B is lost.

Bloco A
if (Pergunta) {
    Bloco B
} else {
    Bloco C
}

This type of problem is very common, and most modern processors rely on various techniques of leap prediction, to try to "predict" beforehand whether the result to a question will be true or false before starting the decoding process of pipeline.

Still, this process does not always manage to match the forecast with 100% accuracy, especially if the Pergunta is directly related to the result of some arithmetic operation at the end of Bloco A.

In such cases, the pipeline may not even be completely ruled out, but may suffer Stall, which causes its operation to stop momentarily, waiting for the end of the instruction on which it depends.

Chapter 7 of the book The Software Optimization Cookbook - Second Edition discusses a number of techniques and tricks to deal with these issues.

Although it is not always possible to use some of the book’s techniques in languages such as C#, Java, VB, the general idea of how pipeline, leap prediction and Stall applies in the same way to all of them.

Just to conclude, leaving the context of computational architecture, and returning to the context of object orientation of the question, I realized that, despite using an analogous functionality, no answer explicitly cites the use of design standards like the Strategy, to remove constructions if, as explained in the book Design Patterns: Elements of Reusable Object-Oriented Software ("GOF book, "as it is also known).

9

Excess IF or CASE does increase code complexity, but Object-Oriented methodologies, for example, can simplify code when you use Inheritance and Polymorphism. The difference is that instead of having many IFS and CASES you get "many classes". Having many classes is not bad because each of them will have a specific function and the semantics of your code can be clearer. In addition you can use "Design Patterns" to promote good design practices making your code more understandable.

See for example the case of implementing an Application to override documents of various types. A purely structured approach would require a number of Ifs or Cases to handle the different types of documents.

On the other hand in an Object-Oriented approach we can use the pattern Factory Method to encapsulate all logic regarding the creation of a given document type and the Polymorphism implemented by the Programming Language infrastructure used will be responsible for doing much of the decision work of its series of Ifs and Cases of the structured approach. The gain in code clarity is invaluable as a simple UML Class diagram shows the implementation and it is not necessary to view the code in detail to understand how the system works.

Diagrama UML para nossa Aplicação

Note that in this example we created only a concrete class Meudocumento but we could have several different concrete classes and the application would continue to know only the Abstraction Document.

Each Design Pattern helps improve a specific part of an application this is a simple example with a single Pattern. In an application we can use dozens of Design Patterns collaborating with each other.

  • Working in Design to improve the application by removing the excess of Ifs and Cases requires an understanding of Design Patterns and can be done in code Refactoring which is already working without proper optimization.

  • 4

    It would be possible to elaborate a little more. The answer seems good, but it remains to say more why of each case or show the differences.

  • 1

    Much better now!

9

Particularly, I found two interesting ways to eliminate unnecessary if’s.

1 - Polymorphism

First an example using an outdated technique involving numerous if’s and difficult to maintain:

public class Animal {
    private String tipo;
    public Animal(String tipo) {
        this.tipo = tipo;
    }
    public void fazerBarulho() {
        if(tipo.equalsIgnoreCase("Cachorro")) {
            System.out.println("Au! Au!")
        }
        else if(tipo.equalsIgnoreCase("Gato")) {
            System.out.println("Miau!")
        }
        else if(tipo.equalsIgnoreCase("Galinha")) {
            System.out.println("Pó pó pó");
        }
    }
}

The code could be simplified with the help of polymorphism, as follows:

polimorfismo

public class Teste {
    Animal cachorro = new Cachorro();
    Animal gato = new Gato();
    Animal galinha = new Galinha();

    System.out.println(cachorro.fazerBarulho());
    System.out.println(gato.fazerBarulho());
    System.out.println(galinha.fazerBarulho());
}

When you call the method fazerBarulho() is called the method of the object in question, even because there is no way to implement the method in the interface, so the result of the above code would be:

Au! Au!
Meow!
Powdered

2 - Remodeling its structure

Another option to reduce the if’s is by remodeling its structure. Follow an example:

Imagine that you developed a software for your company that would be a product configurator, style those of cars that you ride your.

In the accessories part you did something like this:

acessorios

And you let users choose their accessories freely.

One fine day you are feeding your database with new accessories and you get the information from your boss that two of them are incompatible with each other. Say direção hidráulica and direção elétrica. You cannot let the customer choose both, so you must restrict this in your software.

You decide not to remodel your structure and prefer to make a small "repair" in your programming:

Map<Integer, JCheckBox> checkBoxesAcessorios = new HashMap<>();

.
.
.

//digamos que o id dos acessórios incompatíveis são 74 e 75

//nesse if voce verifica se o acessorio eh o 74 e desmarca o 75, caso ele esteja marcado
if(acessorio.getId() == 74) {
    checkBoxesAcessorios().get(75).setSelected(false);
}
//nesse if voce desmarca o 74 caso o acessorio seja o 75
else if(acessorio.getId() == 75) {
    checkBoxesAcessorios().get(74).setSelected(false);
}

It is no surprise to anyone that this code will work well, and the task will have been accomplished.

However, suppose that for your unhappiness, each day that passes begins to appear more and more products that fit the condition of incompatibility and you find yourself obliged to fill your code with zilhares of if’s, the first thing you notice at this point is that you have become a slave to your code, because every time new accessories with incompatibility are inserted you must change it.

Tired of fiddling with your code with each new accessory you decide to remodel your structure, since your program should not only meet the needs it met when it was designed, the program needs to evolve to meet new needs and this requires a remodel. One option would be to change your Accessory class, leaving it like this:

acessorios com grupo

An attribute has been added grupo which indicates which accessories belong to the same group and therefore cannot be used at the same time. The part of the code that possessed the numerous if’s would look like this:

desmarcarChecksIncompativeis(acessorio.getGrupo());

And a possible implementation of the above method would be:

public void desmarcarChecksIncompativeis(int grupo) {
    for(Acessorios a: this.getAcessorios()) {
        if(a.getGrupo() == grupo) {
            checkBoxesAcessorios().get(a.getId()).setSelected(false);
        }
    }
}

Modelling in this way, it is no longer necessary to change the code to indicate which accessories are incompatible, simply register them in the database within the same group, then every time an accessory is selected the incompatible accessories will be removed.

  • The polymorphism has already been mentioned in the accepted answer, but I chose to quote it again because I wanted to create a leaner example. The second nobody had mentioned.

  • I think it’s because 2nd. It eliminates the if:) It’s just somewhere else. It’s like the example of for of Luiz Vieira, did not eliminate the for, just switched places. I had voted for her because of the classical polymorphism part. In a certain way the second is a polymorphism of the object but that only works in specific situations. I do not think the goal of eliminating the if be this. Anyway you can do more flexibly with lambda. He even wanted to answer because he still has an answer with it. But it is not the right solution. Your reply, the carlosrafaelgn and the hugomg should have + votes.

  • @moustache yes, I only I switched the if place, if they have 1000 accessories the condition must be checked the if will run 1000 times, but it is only written on the code line once, this along with the new field grupo of the object reflect in an infinitely better maintainability because the code does not need to change each time the BD is updated ;-) About the lambda, I’m a little outdated and do not know exactly what it is, If you share an answer you can update who is lost as soon as me :)

7

This depends a lot on the languages. In that reply the other question, I gave an example of how ifs can be abused in languages that support Interfaces (such as C#, Visual Basic.NET and Java).

In other cases, I would say you should avoid iflong s, as they take the linearity out of the code. If you have a function of 50 lines, in which in the fifth line you have a if which has 20 lines, and then a else with another 20, it is quite possible that this can be optimized, for example. If the operations are similar, can’t you apply a use of interfaces in that medium and simply remove the conditions? (See my example in the other answer, if it is not clear)

Another case, as mentioned in another answer, is when there are long currents of if-else-if. In such cases, a switch (or, in functional languages, pattern match) is something that best suits your needs.

I, as personal practice, do not use ifs in my code (except when my leader forces me). I always use the ternary conditional operator ?: available in almost all languages. The main advantage is that it always returns a value, which gives you beautiful advantages of code:

if (condição) {
  valor = 1;
} else {
  valor = 2;
}

Against:

valor = condição ? 1 : 2;

In short: As I said in the second paragraph and in one comment, the problem of if is the same as goto: Lose the linearity of the code. When you have blocks of if (and / or else / else-if) very large, getting lost in code simulating the execution flow is very easy.

  • 3

    "I, as a personal practice, don’t use ifs in my code[...]". I was curious about that. How would you do, for example, in a situation where an object can have two states? Type, connected and disconnected. And certain operations on this object depend on whether it’s connected or not. You would make two Connected/Disconnected classes and inherit from a common interface?

  • @Franciscojunior Boa. Since there are only two cases (connected and disconnected) I would probably use the ternary conditional operator (calling a function if necessary). If I had more cases (connected, disconnected and limited), I would probably use a Switch / Pattern Match or Array / Dictionary as quoted in Pauloroberto’s reply. (In short: The problem is not conditions, but losing the linearity of the code. I don’t like having to "ignore" more than 5 lines to imagine the result of a function, for example.)

  • The operador condicional ternário is a disguised if. What the question expects is a way to change the structure of the code so as not to have to do so many tests on the variables in the course of the code. This is possible through object-oriented programming (POO) and how to do this using POO will be the correct answer.

  • 1

    @Math Is Not only one if disguised as function. It is an expression, while the if is just a flow control. It always returns a value while the if only changes the path the program takes (as well as a goto) based on one condition. If you want POO alternatives to your problem, I put this in the first paragraph. But the question asks, in the title, an explanation of "why in some ifs situations are considered bad". And this I answer clearly in the summary.

-5

Boy, this is very relative, for example instead of writing

<?php
$str = 'f';
if ($str == 'a') {
    echo 'Letra A';
} else if ($str == 'b') {
    echo 'Lebra B';
}
// ....

Is the most appropriate

<?php
switch ($str) {
    case 'a': echo 'Letra A';
    case 'b': echo 'Letra B';
    // .....
}

Good practices is with the experience and a certain "Feeling" of the business

  • 16

    This is not what the question is talking about. Although there is a slightly different semantics, switch is practically an if. Moreover, it remains to explain why one better than the other. This answer does not answer the question.

  • A switch is more easily vectored by the compiler or interpreter. Certainly a switch with 26 options going from 'A' to 'Z' is better than an if/Else with 26 items, but the best is a more efficient and beautiful handler vector-style construction.

Browser other questions tagged

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