I have a java class with a thousand line method of if/else logic like this:
if (userType == \"admin\") {
if (age > 12) {
if (location == \"
Based just on the variable names, I'm guessing that you should subclass User
(or whatever it is that has a userType
variable) into AdminUser
and StudentUser
(and possibly others) and use polymorphism.
First - use enums for userType and location - then you can use switch statements (improves readability)
Second - use more methods.
Example:
switch (userType) {
case Admin: handleAdmin(); break;
case Student: handleStudent(); break;
}
and later
private void handleAdmin() {
switch (location) {
case USA: handleAdminInUSA(); break;
case Mexico: handleAdminInMexico(); break;
}
}
Further, identify duplicate code and put it in extra methods.
EDIT
If someone forces you to code Java without enums (like you're forced to use Java 1.4.2), use 'final static's instead of enums or do something like:
if (isAdmin(userType)) {
handleAdmin(location, age);
} else if (isStudent(userType)) {
handleStudent(location, age));
}
//...
private void handleAdmin(String location, int age) {
if (isUSA(location)) {
handleAdminInUSA(age);
} else if (isUSA(location)) {
handleAdminInMexico(age);
}
}
//...
private void handleAdminInUSA(int age) {
if (isOldEnough(age)) {
handleAdminInUSAOldEnough();
} else if (isChild(age)) {
handleChildishAdminInUSA(); // ;-)
} //...
}
The first thing I would do with this code is create the types Admin
and Student
, both of which inherit from the base type User
. These classes should have a doStuff()
method where you hide the rest of this logic.
As a rule of thumb, any time you catch yourself switching on type, you can use polymorphism instead.
The risk of this is not just that it is unsightly, but that it is very error prone. After a while, you could run into a risk of overlaps in your conditions.
If you can really distinguish the condition by user type, you can at the minimum break the body of each condition into a separate function. So that you check based on the type, and call an appropriate function specific to that type. A more OO solution is to represent each user as a class, and then override some calculation method to return a value based on the age. If you can't use classes but can at least use enums, then you will be able to do a nicer switch statement on the enums. Switches on Strings will only come in Java 7.
What worries me is situations of overlaps (e.g., two user types with some shared rules, etc.). If that ends up being the case, you might be better off representing the data as some external file (E.g., a table) which you would read and maintain, and your code will essentially operate as a driver that does the appropriate lookup in this data set. This is a common approach for complex business rules, since nobody wants to go and maintain tons of code.
You really need to break these cases into object methods. I'm assuming these strings and numbers are being pulled out of a database. Instead of using them in their raw form in giant nested conditional logic, you need to use these pieces of data to construct objects that model the desired interactions. Consider a UserRole class with a StudentRole and AdminRole subclasses, a Region class with USA and Mexico subclasses, and an AgeGroup class with appropriate partitioned subclasses.
Once you have this object oriented structure in place, you'll be able to make use of well understood object oriented design patterns to re-factor this logic.
Take a look at the Visitor pattern. It makes use of polymorphism but is a little more flexible in that it is easier to add new cases later.
The downside is you'd need some way to convert the state info into different instances. The benefit is a cleaner way to add behavior without having to modify your inheritance hierarchy.