Many if statements - refactoring Yesterday I found some nice video from some IT conference about removing if statements from code. I saw a very nice example of refactoring. I am curious what do you think about it, especially what weak points do you see in such approach.

1) We have a class with very "nice" method:

public int inferCategoryID(Component c) {
        if(c.hasOneChannel() &amp;&amp; c.hasValidCertificate() &amp;&amp; c.isTested() &amp;&amp; c.frequentlyUsed()) {
            return 24;
        }
        if(c.hasOneChannel() &amp;&amp; c.hasValidCertificate() &amp;&amp; !c.isTested() &amp;&amp; c.frequentlyUsed()) {
            return 95;
        }
        if(c.hasOneChannel() &amp;&amp; !c.hasValidCertificate() &amp;&amp; c.isTested() &amp;&amp; c.frequentlyUsed()) {
            return 221;
        }
        if(c.hasTwoChannels() &amp;&amp; c.hasValidCertificate() &amp;&amp; c.isTested() &amp;&amp; c.frequentlyUsed()) {
            return 2004;
        }
        if(c.hasTwoChannels() &amp;&amp; c.hasValidCertificate() &amp;&amp; !c.isTested() &amp;&amp; c.frequentlyUsed()) {
            return 20044;
        }
        if(c.hasTwoChannels() &amp;&amp; c.hasValidCertificate() &amp;&amp; !c.isTested() &amp;&amp; !c.frequentlyUsed()) {
            return 2003;
        }
        // etc...
    }


As you can see all conditions are mutually exclusive. 

2) The idea was, to create a separate class for each condition - under a common interface:

interface CategoryIDCondition  {
        public boolean isTrue(Component c);
        public int getID();
    }

class Cat24Condition implements CategoryIDCondition {
    public boolean isTrue(Component c){
        return c.hasOneChannel() 
                &amp;&amp; c.hasValidCertificate() 
                &amp;&amp; c.isTested() 
                &amp;&amp; c.frequentlyUsed();
    }

    public int getID(){
        return 24;
    }
}


// etc...

3) Usage example: 

public int inferCategoryID(Component c) {
        List&lt;CategoryIDCondition&gt; conditions = new ArrayList&lt;&gt;(); 
        conditions.add(new Cat24Condition);
        conditions.add(new Cat95Condition);
        // etc... (btw. not a good place for list init but never mind...)

        for (CategoryIDCondition cond : conditions) {
            if(cond.isTrue(c)){
                return cond.getID();
            }
        }
    }


What weak points do you see in such approach apart from that, that conditions have to be mutually exclusive, or have to be added to conditions list in a very specific order? Would you decide to use such strategy in your code?
Personally, I wouldn't use that approach, because of the amount of boilerplate code and complexity it introduces. Adding N+1 classes, where N is the number of conditions, just seems like overkill to me.

First off, I don't think that the method at hand must necessarily be refactored. I agree that is not a terribly "nice" method from a aesthetic point of view, but the amount of complexity it contains might not justify a refactor. Often, a bit of reformatting is already a good approach:

public int inferCategoryID(Component c) {
    if( c.hasOneChannel() &amp;&amp;
        c.hasValidCertificate() &amp;&amp;
        c.isTested() &amp;&amp;
        c.frequentlyUsed()) {
        return 24;
    }

    [...]
}


If that is not satisfactory, my next approach would've been to simply used separate methods for the conditions. This doesn't imply creating lots of classes, but still clean up the inferCategoryID substantially. While I'm not a fan of giving names like isCat24, I will use it here since the example refactoring technique using classes does so too and I can't infer more useful naming from the conditions:

public int inferCategoryID(Component c) {
    if(isCat24(c)) {
        return 24;
    }

    [...]
}

public boolean isCat24(Component c) {
    return c.hasOneChannel() &amp;&amp;
           c.hasValidCertificate() &amp;&amp;
           c.isTested() &amp;&amp;
           c.frequentlyUsed();
}


Last but not least, in cases where lots of boolean conditions are used, bit-lists are often applicable. The following code isn't tested, but it should give an idea of what I mean:

const int CATEGORY_24 = 15;
const int CATEGORY_95 = 13;

public int inferCategoryID(Component c) {
    int properties = componentProperties(c);

    if(properties == CATEGORY_24) {
        return 24;
    }

    if(properties == CATEGORY_95) {
        return 95;
    }

    [...]
}

public int componentProperties(Component c) {
    return ((int) c.hasOneChannel() &lt;&lt; 3) &amp;&amp;
           ((int) c.hasValidCertificate() &lt;&lt; 2) &amp;&amp;
           ((int) c.isTested() &lt;&lt; 1) &amp;&amp;
           ((int) c.frequentlyUsed());
}


The integer returned from componentProperties is a dense representation of the components properties in form of a bit list, that is each bit represents one boolean property. For example, if all properties are true, the resulting integer (in binary) would be 1111, if only hasOneChannel was true, the integer would be 1000 and so on. This has the advantage that you don't have to create a large amount of methods or classes, but still cleans up the inferCategoryID method quite nicely. The downside of this method is that it is arguably the most cryptic (while I also find the multiple-class approach relatively cryptic :D).

Since I said that adding lots of classes introduces too much complexity to be really worth it (in this case), I can't say that the last solution is any different in terms of adding unnecessary complexity, so I probably wouldn't use either of them for the use case at hand. I just wanted to give you an idea of a different approach that, in my opinion, is preferable to creating a class per condition :)