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

      booleanValue = true;

    }


    public Object get() {

      return booleanValue;

    }

  }


  private class StringArgumentMarshaler extends ArgumentMarshaler {

    private String stringValue = "";


    public void set(String s) {

      stringValue = s;

    }


    public Object get() {

      return stringValue;

    }

  }


  private class IntegerArgumentMarshaler extends ArgumentMarshaler {


    public void set(String s) {

    }


    public Object get() {

      return null;

    }

  }

}

Осталось лишь повторить этот процесс для integer. На этот раз задача немного усложняется: целые числа необходимо разбирать, а в ходе разбора возможны исключения. Но внешний вид кода улучшается тем, что вся концепция NumberFormatException скрыта в классе IntegerArgumentMarshaler.

private boolean isIntArg(char argChar) {return intArgs.containsKey(argChar);}


  private void setIntArg(char argChar) throws ArgsException {

    currentArgument++;

    String parameter = null;

    try {

      parameter = args[currentArgument];

      intArgs.get(argChar).set(parameter);

    } catch (ArrayIndexOutOfBoundsException e) {

      valid = false;

      errorArgumentId = argChar;

      errorCode = ErrorCode.MISSING_INTEGER;

      throw new ArgsException();

    } catch (ArgsException e) {

      valid = false;

      errorArgumentId = argChar;

      errorParameter = parameter;

      errorCode = ErrorCode.INVALID_INTEGER;

      throw e;

    }

  }

...

  private void setBooleanArg(char argChar) {

try {

      booleanArgs.get(argChar).set("true");

    } catch (ArgsException e) {

    }

  }

...

  public int getInt(char arg) {

    Args.ArgumentMarshaler am = intArgs.get(arg);

    return am == null ? 0 : (Integer) am.get();

  }

...

  private abstract class ArgumentMarshaler {

    public abstract void set(String s) throws ArgsException;

    public abstract Object get();

  }

...

  private class IntegerArgumentMarshaler extends ArgumentMarshaler {

    private int intValue = 0;


    public void set(String s) throws ArgsException {

      try {

        intValue = Integer.parseInt(s);

      } catch (NumberFormatException e) {

        throw new ArgsException();

      }

    }

    public Object get() {

      return intValue;

    }

  }

Конечно, тесты по-прежнему проходили. Далее я избавился от трех разновидностей Map в начале алгоритма, отчего система стала намного более универсальной. Впрочем, я не мог их просто удалить, поскольку это нарушило бы работу системы. Вместо этого я добавил новый объект Map для ArgumentMarshaler, а затем последовательно изменял методы, чтобы они использовали этот объект вместо трех исходных.

public class Args {

...

  private Map booleanArgs =

    new HashMap();

  private Map stringArgs =

    new HashMap();

  private Map intArgs =

    new HashMap();

  private Map marshalers =

    new HashMap();

...

  private void parseBooleanSchemaElement(char elementId) {

    ArgumentMarshaler m = new BooleanArgumentMarshaler();

    booleanArgs.put(elementId, m);

    marshalers.put(elementId, m);

  }


  private void parseIntegerSchemaElement(char elementId) {

    ArgumentMarshaler m = new IntegerArgumentMarshaler();

    intArgs.put(elementId, m);

    marshalers.put(elementId, m);

  }


  private void parseStringSchemaElement(char elementId) {

    ArgumentMarshaler m = new StringArgumentMarshaler();

    stringArgs.put(elementId, m);

    marshalers.put(elementId, m);

  }

Разумеется, тесты проходили успешно. Далее я привел метод isBooleanArg:

private boolean isBooleanArg(char argChar) {

  return booleanArgs.containsKey(argChar);

}

к следующему виду:

private boolean isBooleanArg(char argChar) {

  ArgumentMarshaler m = marshalers.get(argChar);

  return m instanceof BooleanArgumentMarshaler;

}

Тесты по-прежнему проходят. Я внес аналогичные изменения в isIntArg и isStringArg.

private boolean isIntArg(char argChar) {

  ArgumentMarshaler m = marshalers.get(argChar);

  return m instanceof IntegerArgumentMarshaler;

}


private boolean isStringArg(char argChar) {

  ArgumentMarshaler m = marshalers.get(argChar);

  return m instanceof StringArgumentMarshaler;

}

Тесты проходят. Я удалил все повторяющиеся вызовы marshalers.get:

private boolean setArgument(char argChar) throws ArgsException {

  ArgumentMarshaler m = marshalers.get(argChar);

  if (isBooleanArg(m))

    setBooleanArg(argChar);

  else if (isStringArg(m))

    setStringArg(argChar);

  else if (isIntArg(m))

    setIntArg(argChar);

  else

    return false;


  return true;