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


    } catch (ArgsException e) {

      valid = false;

      errorArgumentId = argChar;

      throw e;

    }

    return true;

  }

---

  private void setBooleanArg(ArgumentMarshaler m,

                             Iterator currentArgument)

                             throws ArgsException {

  try {

      m.set("true");

  catch (ArgsException e) {

}

}

Но ведь мы только что перенесли обработку исключения в функцию? Ситуация с включением того, что вы намереваетесь вскоре исключить, весьма часто встречается при переработке кода. Малый размер шагов и необходимость прохождения тестов означает, что вам придется часто перемещать туда-сюда фрагменты кода. Переработка кода напоминает кубик Рубика: чтобы добиться большой цели, необходимо выполнить множество мелких операций. Каждая операция делает возможной следующую.

Зачем передавать итератор, если setBooleanArg он не нужен? Потому что он нужен setIntArg и setStringArg! И если я хочу организовать доступ ко всем трем функциям через абстрактный метод в ArgumentMarshaller, мне не обойтись без его передачи setBooleanArg.

Итак, функция setBooleanArg стала бесполезной. Если бы в ArgumentMarshaler присутствовала функция set, то мы могли бы вызвать ее напрямую. Значит, нужно создать такую функцию! Первым шагом станет включение нового абстрактного метода в ArgumentMarshaler.

private abstract class ArgumentMarshaler {

  public abstract void set(Iterator currentArgument)

                       throws ArgsException;

  public abstract void set(String s) throws ArgsException;

  public abstract Object get();

}

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

private class BooleanArgumentMarshaler extends ArgumentMarshaler {

  private boolean booleanValue = false;


  public void set(Iterator currentArgument) throws ArgsException {

  booleanValue = true;

  }


  public void set(String s) {

    booleanValue = true;

  }


  public Object get() {

    return booleanValue;

  }

}


private class StringArgumentMarshaler extends ArgumentMarshaler {

  private String stringValue = "";


  public void set(Iterator currentArgument) throws ArgsException {

  }


  public void set(String s) {

    stringValue = s;

  }


  public Object get() {

    return stringValue;

  }

}


private class IntegerArgumentMarshaler extends ArgumentMarshaler {

  private int intValue = 0;


  public void set(Iterator currentArgument) throws ArgsException {

  }


  public void set(String s) throws ArgsException {

    try {

      intValue = Integer.parseInt(s);

    } catch (NumberFormatException e) {

      throw new ArgsException();

    }

  }

  public Object get() {

    return intValue;

  }

}

А теперь setBooleanArg можно удалить!

private boolean setArgument(char argChar) throws ArgsException {

  ArgumentMarshaler m = marshalers.get(argChar);

  if (m == null)

    return false;

  try {

    if (m instanceof BooleanArgumentMarshaler)

      m.set(currentArgument);

    else if (m instanceof StringArgumentMarshaler)

      setStringArg(m);

    else if (m instanceof IntegerArgumentMarshaler)

      setIntArg(m);


  } catch (ArgsException e) {

    valid = false;

    errorArgumentId = argChar;

    throw e;

  }

  return true;

}

Все тесты проходят, а функция set размещается в BooleanArgumentMarshaler! Теперь можно сделать то же самое для String and Integer.

private boolean setArgument(char argChar) throws ArgsException {

  ArgumentMarshaler m = marshalers.get(argChar);

  if (m == null)

    return false;

  try {

    if (m instanceof BooleanArgumentMarshaler)

      m.set(currentArgument);

    else if (m instanceof StringArgumentMarshaler)

      m.set(currentArgument);

    else if (m instanceof IntegerArgumentMarshaler)

      m.set(currentArgument);


  } catch (ArgsException e) {

    valid = false;

    errorArgumentId = argChar;

    throw e;

  }

  return true;

}

---

private class StringArgumentMarshaler extends ArgumentMarshaler {

  private String stringValue = "";


  public void set(Iterator currentArgument) throws ArgsException {

    try {

      stringValue = currentArgument.next();

    } catch (NoSuchElementException e) {

      errorCode = ErrorCode.MISSING_STRING;

      throw new ArgsException();

    }

  }


  public void set(String s) {

  }


  public Object get() {

    return stringValue;

  }

}


private class IntegerArgumentMarshaler extends ArgumentMarshaler {

  private int intValue = 0;

  public void set(Iterator currentArgument) throws ArgsException {

    String parameter = null;

    try {

      parameter = currentArgument.next();

      set(parameter);

    } catch (NoSuchElementException e) {

      errorCode = ErrorCode.MISSING_INTEGER;

      throw new ArgsException();

    } catch (ArgsException e) {

      errorParameter = parameter;

      errorCode = ErrorCode.INVALID_INTEGER;

      throw e;

    }

  }