заявление "если" против ОО Дизайн - PullRequest
7 голосов
/ 22 октября 2010

У меня есть enum скажет ErrorCodes, что

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
}

теперь я проверяю коды ошибок моего исключения с помощью этих кодов ошибок. Я не хочу писать, если это сделать, если это сделать это. Как я могу решить эту проблему (писать 10+, если блоки)

Есть ли какая-то модель дизайна в этой ситуации?

Спасибо

Ответы [ 6 ]

7 голосов
/ 22 октября 2010

Либо вы делаете это с помощью оператора if или переключателя, либо вы просто каким-то образом внедрили соответствующую логику в ErrorCode.

В ОО-режиме все зависит от того, как вы хотите, чтобы приложение или система реагировали на код ошибки. Допустим, вы просто хотите, чтобы он выводил некое диалоговое окно:

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
  }

}

Вместо этого мы могли бы создать класс ErrorHandler, который делает это вместо:

// 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";
  }
}

Прежнее решение легко реализовать, но его становится сложно поддерживать по мере увеличения кода. Последнее решение является более сложным (aka. Шаблонный шаблон по принципу Open-Closed ), но позволяет вам добавлять дополнительные методы в ErrorHandler, когда вам это нужно (например, как восстановление ресурсов или что-то еще). Вы также можете реализовать это с помощью шаблона стратегии .

Вы не сможете полностью избавиться от условных операторов, но в последнем случае условное выражение будет добавлено в ту часть кода, где возникла ошибка. Таким образом, у вас не будет двойного обслуживания условных операторов как у инициатора, так и у кода обработки ошибок.

EDIT:

См. этот ответ Майкла Боргвардта и этот ответ Оксайта о том, как реализовать методы в перечислениях Java, если вы хотите сделать это вместо этого.

4 голосов
/ 22 октября 2010

Перечисления Java очень мощные и допускают реализации методов для каждого экземпляра:

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();
}

Тогда вы можете просто позвонить errorCode.handleError();. Однако сомнительно, действительно ли перечисление ErrorCode является правильным местом для этой логики.

3 голосов
/ 22 октября 2010

Как указывает Spoike, использование полиморфизма для выбора правильного метода обработки ошибок является опцией.Этот подход в основном откладывает 10+ блоков if для поиска виртуальных методов JVM, определяя иерархию классов.

Но прежде чем перейти к полноценной иерархии классов, также рассмотрите возможность использования enum методов.Эта опция хорошо работает, если то, что вы планируете делать в каждом случае, довольно схоже.

Например, если вы хотите возвращать разные сообщения об ошибках для каждого ErrorCode, вы можете просто сделать это:

// 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;
   }
}

Тогда ваш код обработки ошибок становится просто:

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

Один из недостатков этого подхода заключается в том, что если вы хотите добавить дополнительные регистры, вам нужно будет изменить само перечисление, тогда как с помощьюв иерархии классов вы можете добавлять новые случаи, не изменяя существующий код.

2 голосов
/ 22 октября 2010

Я считаю, что лучшее, что вы можете сделать, - это реализовать шаблон стратегии. Таким образом, вам не придется изменять существующие классы при добавлении новых перечислений, но вы все равно сможете их расширять. (Разомкнут-принцип).

Поиск паттерна стратегии и открытого закрытого принципа.

0 голосов
/ 22 октября 2010

По моему мнению, в ErrorCodes нет ничего плохого, как перечисления и оператор switch для диспетчеризации обработки ошибок. Перечисления и переключатели отлично сочетаются друг с другом.

Тем не менее, может быть, вы найдете следующее интересное (вид чрезмерного дизайна), см. Пример или «Двойная отправка» в Википедии . Предполагаемые требования:

  • Обработка ошибок должна быть заключена в собственный класс
  • Обработка ошибок должна быть заменяемой
  • Безопасность типов: всякий раз, когда добавляется ошибка, вы вынуждены добавлять обработку ошибок при каждой реализации обработчика ошибок. Невозможно «забыть» об ошибке в одном (из многих) параметров переключателя.

код:

//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 голосов
/ 22 октября 2010

Вы можете создать карту кодов ошибок (целое число) для типов перечислений

Редактировать

В этом решении, когда карта подготовлена, вы можете искатькод ошибки на карте и, следовательно, не потребует, если ... еще поиск поднимает.нужно, это,

ErrorCodes error = errorMap.get(erro_code_from_application);

Таким образом, устраняя необходимость для всех if..else.

Вам просто нужно настроить карту таким образом, чтобы добавление кодов ошибок нетребуют изменений в другом коде.Подготовка карты является однократным действием и может быть связана с базой данных, файлом свойств и т. Д. Во время инициализации вашего приложения

...