Чистый код. Создание, анализ и рефакторинг — страница 47 из 94

                             errorArgumentId);

      case INVALID_INTEGER:

        return String.format("Argument -%c expects an integer but was '%s'.",

                             errorArgumentId, errorParameter);

      case MISSING_INTEGER:

        return String.format("Could not find integer parameter for -%c.",

                             errorArgumentId);

      case INVALID_DOUBLE:

        return String.format("Argument -%c expects a double but was '%s'.",

                             errorArgumentId, errorParameter);

      case MISSING_DOUBLE:

        return String.format("Could not find double parameter for -%c.",

                             errorArgumentId);

    }

    return "";

  }

Тесты успешно проходят. Следующий тест проверяет, что ошибка с отсутствующим аргументом double будет успешно обнаружена:

public void testMissingDouble() throws Exception {

  Args args = new Args("x##", new String[]{"-x"});

  assertFalse(args.isValid());

  assertEquals(0, args.cardinality());

  assertFalse(args.has('x'));

  assertEquals(0.0, args.getDouble('x'), 0.01);

  assertEquals("Could not find double parameter for -x.",

               args.errorMessage());

}

Как и ожидалось, все проходит успешно. Этот тест был написан просто для полноты картины.

Код исключения некрасив, и в классе Args ему не место. Также в коде инициируется исключение ParseException, которое на самом деле нам не принадлежит. Давайте объединим все исключения в один класс ArgsException и переместим его в отдельный модуль.

public class ArgsException extends Exception {

  private char errorArgumentId = '\0';

  private String errorParameter = "TILT";

  private ErrorCode errorCode = ErrorCode.OK;


  public ArgsException() {}


  public ArgsException(String message) {super(message);}


  public enum ErrorCode {

    OK, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT,

    MISSING_DOUBLE, INVALID_DOUBLE}

}

---


public class Args {

  ...

  private char errorArgumentId = '\0';

  private String errorParameter = "TILT";

  private ArgsException.ErrorCode errorCode = ArgsException.ErrorCode.OK;

  private List argsList;


  public Args(String schema, String[] args) throws ArgsException {

    this.schema = schema;

    argsList = Arrays.asList(args);

    valid = parse();

  }


  private boolean parse() throws ArgsException {

    if (schema.length() == 0 && argsList.size() == 0)

      return true;

    parseSchema();

    try {

      parseArguments();

    } catch (ArgsException e) {

    }

    return valid;

  }

  private boolean parseSchema() throws ArgsException {

    ...

  }


  private void parseSchemaElement(String element) throws ArgsException {

    ...

    else

      throw new ArgsException(

        String.format("Argument: %c has invalid format: %s.",

                      elementId,elementTail));

  }


  private void validateSchemaElementId(char elementId) throws ArgsException {

    if (!Character.isLetter(elementId)) {

      throw new ArgsException(

        "Bad character:" + elementId + "in Args format: " + schema);

    }

  }


  ...

  private void parseElement(char argChar) throws ArgsException {

    if (setArgument(argChar))

      argsFound.add(argChar);

    else {

      unexpectedArguments.add(argChar);

      errorCode = ArgsException.ErrorCode.UNEXPECTED_ARGUMENT;

      valid = false;

    }

  }

  ...


  private class StringArgumentMarshaler implements ArgumentMarshaler {

    private String stringValue = "";


    public void set(Iterator currentArgument) throws ArgsException {

      try {

        stringValue = currentArgument.next();

      } catch (NoSuchElementException e) {

        errorCode = ArgsException.ErrorCode.MISSING_STRING;

        throw new ArgsException();

      }

    }


    public Object get() {

      return stringValue;

    }

  }


  private class IntegerArgumentMarshaler implements ArgumentMarshaler {

    private int intValue = 0;


    public void set(Iterator currentArgument) throws ArgsException {

      String parameter = null;

      try {

        parameter = currentArgument.next();

        intValue = Integer.parseInt(parameter);

      } catch (NoSuchElementException e) {

        errorCode = ArgsException.ErrorCode.MISSING_INTEGER;

        throw new ArgsException();

      } catch (NumberFormatException e) {

        errorParameter = parameter;

        errorCode = ArgsException.ErrorCode.INVALID_INTEGER;

        throw new ArgsException();

      }

    }


    public Object get() {

      return intValue;

    }

  }


  private class DoubleArgumentMarshaler implements ArgumentMarshaler {

    private double doubleValue = 0;


    public void set(Iterator currentArgument) throws ArgsException {

      String parameter = null;

      try {

        parameter = currentArgument.next();

        doubleValue = Double.parseDouble(parameter);

      } catch (NoSuchElementException e) {