简体   繁体   中英

How to avoid many if-else with many condition

I need some help with designing the logic of my problem.

Model Bean

package com.ashish.model;
public class Model {
    public Integer a,b,c,d;
    public String f,g,h,i,j;
}

Service Class

package com.ashish.service;

import com.ashish.model.Model;
public class Service {
    public StringBuilder query = null;  
    public Service(){
        query = new StringBuilder("Select * from A where ");
    }
    public String build(Model m){
            if(m.a != null&&m.b==null&&m.c==null&&m.d==null&m.e==null&&m.f==null&&m.g==null&&m.h==null&&m.i==null&&m.j==null)
        query.append("a="+m.a);
    if(m.a == null&&m.b!=null&&m.c==null&&m.d==null&m.e==null&&m.f==null&&m.g==null&&m.h==null&&m.i==null&&m.j==null)
        query.append("b="+m.b);
    if(m.a == null&&m.b==null&&m.c!=null&&m.d==null&m.e==null&&m.f==null&&m.g==null&&m.h==null&&m.i==null&&m.j==null)
        query.append("c="+m.c);
    if(m.a == null&&m.b==null&&m.c==null&&m.d!=null&m.e==null&&m.f==null&&m.g==null&&m.h==null&&m.i==null&&m.j==null)
        query.append("d="+m.d);
    if(m.a == null&&m.b==null&&m.c==null&&m.d==null&m.e!=null&&m.f==null&&m.g==null&&m.h==null&&m.i==null&&m.j==null)
        query.append("e="+m.e);
    if(m.a == null&&m.b==null&&m.c==null&&m.d==null&m.e==null&&m.f!=null&&m.g==null&&m.h==null&&m.i==null&&m.j==null)
        query.append("f="+m.f);
    if(m.a == null&&m.b==null&&m.c==null&&m.d==null&m.e==null&&m.f==null&&m.g!=null&&m.h==null&&m.i==null&&m.j==null)
        query.append("g="+m.g);
    if(m.a == null&&m.b==null&&m.c==null&&m.d==null&m.e==null&&m.f==null&&m.g==null&&m.h!=null&&m.i==null&&m.j==null)
        query.append("h="+m.h);
    if(m.a == null&&m.b==null&&m.c==null&&m.d==null&m.e==null&&m.f==null&&m.g==null&&m.h==null&&m.i!=null&&m.j==null)
        query.append("i="+m.i);
    if(m.a == null&&m.b==null&&m.c==null&&m.d==null&m.e==null&&m.f==null&&m.g==null&&m.h==null&&m.i==null&&m.j!=null)
        query.append("j="+m.j);
    if(m.a != null&&m.b!=null&&m.c==null&&m.d==null&m.e==null&&m.f==null&&m.g==null&&m.h==null&&m.i==null&&m.j==null)
        query.append("a="+m.a);query.append(" b="+m.b);
    if(m.a != null&&m.b==null&&m.c!=null&&m.d==null&m.e==null&&m.f==null&&m.g==null&&m.h==null&&m.i==null&&m.j==null)
        query.append("a="+m.a);query.append(" c="+m.c);
    if(m.a != null&&m.b==null&&m.c==null&&m.d!=null&m.e==null&&m.f==null&&m.g==null&&m.h==null&&m.i==null&&m.j==null)
        query.append("a="+m.a);query.append(" d="+m.d);
    // ... 512 lines in this pattern
    return query.toString();

        return query.toString();
    }
}

I want to write public String build(Model m) in such a way so that I would not have to write 512 if-else condition.

Conditions:

  1. All instance variables of Model class can have two value ( null, not null)

  2. They all can be null or they all can be not null.

  3. There would be total 512 combinations ( since every instance variable have two state and there are 9 instance variable so total number of condition would be 2^9 )

  4. Order of the instance variable does not matter.

  5. My project use Java 6 so I can not use switch on String.

I have looked into various pattern but none of them is meeting my requirement.

Thanks for looking

A private helper method as follows should do it -

private void appendIfNotNull(String fieldOp, String val) {
    if(val != null) {
        query.append(fieldOp).append(val);
    }
}

Then just call it in the build method -

public String build(Model m) {
    appendIfNotNull("a=", m.a); //no null check, just need to repeat this for all fields

Maybe you want to try to use Java Reflection to read all fields of your Model and read them. You don't need to know the field name to read it. So it would be fully dynamic and generic, even if you extend your Model class.

    Class modelClass = Class.forName(Model.class.getName());
    Field[] fields = circleClass.getFields(); //includes all fields declared in you model class
    for (Field f : fields) {
        System.out.println("field " + f.getName() + " has value: " + f.get(<YOUR_MODEL_INSTANCE>));
    }

Example code adapted from: - http://forgetfulprogrammer.wordpress.com/2011/06/13/java-reflection-class-getfields-and-class-getdeclaredfields/

Will this code make sense?

interface ToStringer {
    void appendTo(StringBuilder sb);
}

class NullToStringer implements ToStringer {
    public void appendTo(StringBuilder sb) {
        // Do nothing
    }
}

class IntegerToStringer implements ToStringer {
    private String fieldName;
    private Integer val;
    public IntegerToStringer(String fieldName, Integer val) {
        this.fieldName = fieldName;
        this.val = val;
    }


    public void appendTo(StringBuilder sb) {
        sb.append(field).append(" = ").append(val);
    }
}

public class ToStringFactory {
    public ToStringer getToStringer(String fieldName, Integer val) {
        if (val == null) {
            return new NullToStringer();
        } else {
            return new IntegerToStringer(fieldName, val);
        }
    }       

    public ToStringer getToStringer(String fieldName, String val) {
        ...
    }
}

public String build(Model m){
    ArrayList<ToStringInstance> list = ...;
    list.add(ToStringFactory.getToStringer("f", m.f));
    list.add(ToStringFactory.getToStringer("g", m.g));
    list.add(ToStringFactory.getToStringer("h", m.h));

    StringBuilder sb = ...;

    for (ToStringInstance tsi : list) {
       tsi.appendTo(sb);
    }

    return sb.toString();

}

I am not sure what logic you are trying to implement, but the general approach: creating interface, concrete implementaion of printing values, using NullValue pattern to hide null problem and using factory to control objects creation should do the trick.

By using this approach you can avoid problems with 2^9 combinations by avoiding multiple if-else statements.

Update. Just came to my mind. You can use reflection. Iterate through all fields, get value of each, print it if it is no null. Maybe this will be enough.

As I mentioned in my comment, you don't need a different if for every combination. You just need to append the values that are not null, and ignore the ones that are. Let me know if this works for you.

public String build(Model m) {
    // use this to know when to add " AND " to separate existing values
    boolean appended = false;

    if (m.a != null) {
        query.append("a=" + m.a);
        appended = true;
    }
    if (m.b != null) {
        if (appended) {
            query.append(" AND ");
        }
        query.append("b=" + m.b);
        appended = true;
    }
    if (m.c != null) {
        if (appended) {
            query.append(" AND ");
        }
        query.append("c=" + m.c);
        appended = true;
    }
    if (m.d != null) {
        if (appended) {
            query.append(" AND ");
        }
        query.append("d=" + m.d);
        appended = true;
    }
    if (m.e != null) {
        if (appended) {
            query.append(" AND ");
        }
        query.append("e=" + m.e);
        appended = true;
    }
    if (m.f != null) {
        if (appended) {
            query.append(" AND ");
        }
        query.append("f=" + m.f);
        appended = true;
    }
    if (m.g != null) {
        if (appended) {
            query.append(" AND ");
        }
        query.append("g=" + m.g);
        appended = true;
    }
    if (m.h != null) {
        if (appended) {
            query.append(" AND ");
        }
        query.append("h=" + m.h);
        appended = true;
    }
    if (m.i != null) {
        if (appended) {
            query.append(" AND ");
        }
        query.append("i=" + m.i);
        appended = true;
    }
    if (m.j != null) {
        if (appended) {
            query.append(" AND ");
        }
        query.append("j=" + m.j);
        appended = true;
    }
    return query.toString();
}

It seems like you want to append to the query for each element that is not null . This can be done quite simply with an auxiliary method or two:

public class Service {
    public StringBuilder query = null;  
    public Service(){
        query = new StringBuilder("Select * from A where ");
    }
    public String build(Model m) {
        boolean added = first;
        first &= !maybeAdd("a", m.a, first);
        first &= !maybeAdd("b", m.b, first);
        . . . // all the rest of the fields of m
    }

    /**
     * Add an equality test to an SQL query if the value is not {@code null}.
     * @param key the field name for the query
     * @param value the value to test for equality
     * @param first flag indicating that no conditions have been added
     * @return {@code true} if the value was appended; {@code false} otherwise.
     */    
    private boolean maybeAdd(String key, Object value, boolean first) {
        if (value != null) {
            if (!first) {
                query.append(" AND ");
            }
            query.append(key).append('=').append(value);
            return true;
        }
        return false;
    }
}

Note that if all fields of the model are null , your query will not be correctly formed. You might want to include the appropriate logic in the maybeAdd method to compensate for that.

I don't know why you need two if statements for this:

if( m.a == null) { 
  query.append("m=null");
} else { 
 query.append("m="+m.a);
}
if( m.b == null) { 
  query.append("m=null");
} else { 
 query.append("m="+m.b);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM