开发者

"if" statement vs OO Design

开发者 https://www.devze.com 2023-01-21 02:30 出处:网络
I have enum say ErrorCodes that public enum ErrorCodes { INVALID_LOGIN(100), INVALID_PASSWORD(101), SESSION_EXPIRED(102) ...;

I have enum say ErrorCodes that

public enum ErrorCodes { 
       INVALID_LOGIN(100),
       INVALID_PASSWORD(101),
       SESSION_EXPIRED(102) ...;

       private int errorCode;

       private ErrorCodes(int error){
              this.errorCode = error;
       }    //setter and getter and other codes
}

now I check my exception error codes with this error codes. I don't want to write if this do this, if this do this. How I can solve this 开发者_如何转开发problem (writing 10+ if blocks)

Is there any design patter to that situation ?

Thanks


Either you do it with a if-statement or a switch, or you just implement the logic in question into the ErrorCode somehow.

In an OO fashion it all depends on how you want the application or system react to the error code. Lets say you just want it to output somekind of dialog:

public doSomethingWithError() {

  ErrorCodes e = getError(); 
     // the source of error, or originator, returns the enum

  switch(e) {
    case ErrorCodes.INVALID_LOGIN:
      prompt('Invalid Login');
    case ErrorCodes.INVALID_PASSWORD:
      prompt('Invalid password');
    // and so on
  }

}

We could instead create an ErrorHandler class that does this instead:

// We'll implement this using OO instead
public doSomethingWithError() {

  ErrorHandler e = getError(); 
    // the originator now returns an ErrorHandler object instead

  e.handleMessage();

}

// We will need the following abstract class:
public abstract class ErrorHandler {

   // Lets say we have a prompter class that prompts the message
   private Prompter prompter = new Prompter();

   public final void handleMessage() {
     String message = this.getMessage();
     prompter.prompt(message);
   } 

   // This needs to be implemented in subclasses because
   // handleMessage() method is using it.
   public abstract String getMessage();
}

// And you'll have the following implementations, e.g.
// for invalid logins:
public final class InvalidLoginHandler() {

  public final String getMessage() {
     return "Invalid login";
  }

}

// E.g. for invalid password:
public final class InvalidPasswordHandler() {
  public final String getMessage() {
    return "Invalid password";
  }
}

The former solution is easy to implement, but becomes difficult to maintain as the code grows larger. The latter solution is more complex, (aka. Template Method pattern following the Open-Closed Principle) but enables you to add more methods into the ErrorHandler when you need it (such as restoring resources or whatever). You can also implement this with the Strategy pattern.

You won't get away completely with the conditional statements, but in the latter the conditional is pushed to the part of the code where the error is originated. That way you won't have double maintenance on conditional statements both at the originator and the error handling code.

EDIT:

See this answer by Michael Borgwardt and this answer by oksayt for how to implement methods on Java Enums if you want to do that instead.


Java enums are very powerful and allow per-instance method implementations:

public enum ErrorCode { 
       INVALID_LOGIN {
        public void handleError() {
            // do something
        }
    },
       INVALID_PASSWORD {
        public void handleError() {
            // do something else
        }
    },
       SESSION_EXPIRED {
        public void handleError() {
            // do something else again
        }
    };

    public abstract void handleError();
}

Then you can simply call errorCode.handleError();. However, it is questionable whether an ErrorCode enum is really the right place for that logic.


As pointed out by Spoike, using polymorphism to pick the right error handling method is an option. This approach basically defers the 10+ if blocks to the JVM's virtual method lookup, by defining a class hierarchy.

But before going for a full-blown class hierarchy, also consider using enum methods. This option works well if what you plan to do in each case is fairly similar.

For example, if you want to return a different error message for each ErrorCode, you can simply do this:

// Note singular name for enum
public enum ErrorCode { 
   INVALID_LOGIN(100, "Your login is invalid"),
   INVALID_PASSWORD(101, "Your password is invalid"),
   SESSION_EXPIRED(102, "Your session has expired");

   private final int code;
   private final String 

   private ErrorCode(int code, String message){
          this.code = code;
          this.message = message;
   }

   public String getMessage() {
       return message;
   }
}

Then your error handling code becomes just:

ErrorCode errorCode = getErrorCode();
prompt(errorCode.getMessage());

One drawback of this approach is that if you want to add additional cases, you'll need to modify the enum itself, whereas with a class hierarchy you can add new cases without modifying existing code.


I believe the best you can do is implementing the strategy pattern. This way you won't have to change existing classes when adding new enums but will still be able to extend them. (Open-Closed-Principle).

Search for Strategy Pattern and Open Closed Principle.


You can create a map of error codes(Integer) against enum types

Edit

In this solution, once the map is prepared, you can look up an error code in the map and thus will not require if..else look ups.

E.g.

Map<Integer, ErrorCodes> errorMap = new HashMap<Integer, ErrorCodes>();
for (ErrorCodes error : ErrorCodes.values()) {
     errorMap.put(error.getCode(), error);
}

Now when you want to check an error code coming from your aplpication, all you need to do is,

ErrorCodes error = errorMap.get(erro_code_from_application);

Thus removing the need for all the if..else.

You just need to set up the map in a way that adding error codes doesn't require changes in other code. Preparation of the map is one time activity and can be linked to a database, property file etc during the initialization of your application


In my opinion there is nothing wrong with ErrorCodes as enums and a switch statement to dispatch error handling. Enums and switch fit together really well.

However, maybe you find the following insteresting (kind of over-design), see an Example or "Double dispatching" on Wikipedia. Assumed requirements:

  • Error-handling should be encapsulated in an own class
  • Error-handling should be replacable
  • Type safety: Whenever an error is added, you are forced to add error handling at each error-handler implementation. It is not possible to "forget" an error in one (of maybe many) switch statments.

The code:

//Inteface for type-safe error handler
interface ErrorHandler {
    void handleInvalidLoginError(InvalidLoginError error);
    void handleInvalidPasswordError(InvalidLoginError error);
//One method must be added for each kind error. No chance to "forget" one.
}

//The error hierachy
public class AbstractError(Exception) {
    private int code;
    abstract public void handle(ErrorHandler);
}
public class InvalidLoginError(AbstractError) {
    private String additionalStuff;
    public void handle(ErrorHandler handler) {
        handler.handleInvalidLoginError(this);
    }   
    public String getAdditionalStuff();
}
public class InvalidPasswordError(AbstractError) {
    private int code;
    public void handle(ErrorHandler handler) {
        handler.handleInvalidPasswordError(this);
    }
}

//Test class
public class Test {
    public void test() {
        //Create an error handler instance.
        ErrorHandler handler = new LoggingErrorHandler();

        try {
            doSomething();//throws AbstractError
        }
        catch (AbstractError e) {
            e.handle(handler);
        }
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消