Главная страница
Навигация по странице:

  • private List argsList;

  • if (m == null) return false;

  • Iterator currentArgument)

  • public void set(Iterator currentArgument) throws ArgsException { booleanValue = true;

  • public void set(Iterator currentArgument) throws ArgsException { }

  • m.set(currentArgument); else if (m instanceof IntegerArgumentMarshaler) m.set(currentArgument);

  • String parameter = null; try { parameter = currentArgument.next(); set(parameter); } catch (NoSuchElementException e) {

  • errorCode = ErrorCode.INVALID_INTEGER; throw e; }

  • Создание, анализ ирефакторинг


    Скачать 3.16 Mb.
    НазваниеСоздание, анализ ирефакторинг
    Дата29.09.2022
    Размер3.16 Mb.
    Формат файлаpdf
    Имя файлаChistyj_kod_-_Sozdanie_analiz_i_refaktoring_(2013).pdf
    ТипКнига
    #706087
    страница30 из 49
    1   ...   26   27   28   29   30   31   32   33   ...   49
    261
    parseSchema();
    try {
    parseArguments();
    } catch (ArgsException e) {
    }
    return valid;
    }
    private boolean parseSchema() throws ParseException {
    for (String element : schema.split(",")) {
    if (element.length() > 0) {
    String trimmedElement = element.trim();
    parseSchemaElement(trimmedElement);
    }
    }
    return true;
    }
    private void parseSchemaElement(String element) throws ParseException {
    char elementId = element.charAt(0);
    String elementTail = element.substring(1);
    validateSchemaElementId(elementId);
    if (isBooleanSchemaElement(elementTail))
    marshalers.put(elementId, new BooleanArgumentMarshaler());
    else if (isStringSchemaElement(elementTail))
    marshalers.put(elementId, new StringArgumentMarshaler());
    else if (isIntegerSchemaElement(elementTail)) {
    marshalers.put(elementId, new IntegerArgumentMarshaler());
    } else {
    throw new ParseException(String.format(
    "Argument: %c has invalid format: %s.", elementId, elementTail), 0);
    }
    }
    private void validateSchemaElementId(char elementId) throws ParseException {
    if (!Character.isLetter(elementId)) {
    throw new ParseException(
    "Bad character:" + elementId + "in Args format: " + schema, 0);
    }
    }
    private boolean isStringSchemaElement(String elementTail) {
    return elementTail.equals("*");
    }
    private boolean isBooleanSchemaElement(String elementTail) {
    return elementTail.length() == 0;
    }
    private boolean isIntegerSchemaElement(String elementTail) {
    return elementTail.equals("#");
    }
    продолжение
    261

    262
    Глава 14 . Последовательное очищение
    листинг 14 .12 (продолжение)
    private boolean parseArguments() throws ArgsException {
    for (currentArgument=0; currentArgument String arg = args[currentArgument];
    parseArgument(arg);
    }
    return true;
    }
    private void parseArgument(String arg) throws ArgsException {
    if (arg.startsWith("-"))
    parseElements(arg);
    }
    private void parseElements(String arg) throws ArgsException {
    for (int i = 1; i < arg.length(); i++)
    parseElement(arg.charAt(i));
    }
    private void parseElement(char argChar) throws ArgsException {
    if (setArgument(argChar))
    argsFound.add(argChar);
    else {
    unexpectedArguments.add(argChar);
    errorCode = ErrorCode.UNEXPECTED_ARGUMENT;
    valid = false;
    }
    }
    private boolean setArgument(char argChar) throws ArgsException {
    ArgumentMarshaler m = marshalers.get(argChar);
    try {
    if (m instanceof BooleanArgumentMarshaler)
    setBooleanArg(m);
    else if (m instanceof StringArgumentMarshaler)
    setStringArg(m);
    else if (m instanceof IntegerArgumentMarshaler)
    setIntArg(m);
    else return false;
    } catch (ArgsException e) {
    valid = false;
    errorArgumentId = argChar;
    throw e;
    }
    return true;
    }
    private void setIntArg(ArgumentMarshaler m) throws ArgsException {
    currentArgument++;
    String parameter = null;
    try {
    262

    Аргументы String
    263
    parameter = args[currentArgument];
    m.set(parameter);
    } catch (ArrayIndexOutOfBoundsException e) {
    errorCode = ErrorCode.MISSING_INTEGER;
    throw new ArgsException();
    } catch (ArgsException e) {
    errorParameter = parameter;
    errorCode = ErrorCode.INVALID_INTEGER;
    throw e;
    }
    }
    private void setStringArg(ArgumentMarshaler m) throws ArgsException {
    currentArgument++;
    try {
    m.set(args[currentArgument]);
    } catch (ArrayIndexOutOfBoundsException e) {
    errorCode = ErrorCode.MISSING_STRING;
    throw new ArgsException();
    }
    }
    private void setBooleanArg(ArgumentMarshaler m) {
    try {
    m.set("true");
    } catch (ArgsException e) {
    }
    }
    public int cardinality() {
    return argsFound.size();
    }
    public String usage() {
    if (schema.length() > 0)
    return "-[" + schema + "]";
    else return "";
    }
    public String errorMessage() throws Exception {
    switch (errorCode) {
    case OK:
    throw new Exception("TILT: Should not get here.");
    case UNEXPECTED_ARGUMENT:
    return unexpectedArgumentMessage();
    case MISSING_STRING:
    return String.format("Could not find string parameter for -%c.", errorArgumentId);
    case INVALID_INTEGER:
    return String.format("Argument -%c expects an integer but was '%s'.", errorArgumentId, errorParameter);
    продолжение
    263

    264
    Глава 14 . Последовательное очищение
    листинг 14 .12 (продолжение)
    case MISSING_INTEGER:
    return String.format("Could not find integer parameter for -%c.", errorArgumentId);
    }
    return "";
    }
    private String unexpectedArgumentMessage() {
    StringBuffer message = new StringBuffer("Argument(s) -");
    for (char c : unexpectedArguments) {
    message.append(c);
    }
    message.append(" unexpected.");
    return message.toString();
    }
    public boolean getBoolean(char arg) {
    Args.ArgumentMarshaler am = marshalers.get(arg);
    boolean b = false;
    try {
    b = am != null && (Boolean) am.get();
    } catch (ClassCastException e) {
    b = false;
    }
    return b;
    }
    public String getString(char arg) {
    Args.ArgumentMarshaler am = marshalers.get(arg);
    try {
    return am == null ? "" : (String) am.get();
    } catch (ClassCastException e) {
    return "";
    }
    }
    public int getInt(char arg) {
    Args.ArgumentMarshaler am = marshalers.get(arg);
    try {
    return am == null ? 0 : (Integer) am.get();
    } catch (Exception e) {
    return 0;
    }
    }
    public boolean has(char arg) {
    return argsFound.contains(arg);
    }
    public boolean isValid() {
    return valid;
    }
    264

    Аргументы String
    265
    private class ArgsException extends Exception {
    }
    private abstract class ArgumentMarshaler {
    public abstract void set(String s) throws ArgsException;
    public abstract Object get();
    }
    private class BooleanArgumentMarshaler extends ArgumentMarshaler {
    private boolean booleanValue = false;
    public void set(String s) {
    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 {
    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;
    }
    }
    }
    Вроде бы проделана большая работа, а результат не впечатляет . Структура кода немного улучшилась, но в начале листинга по-прежнему объявляются много- численные переменные; в setArgument осталась кошмарная конструкция проверки типа; функции set выглядят просто ужасно . Я уже не говорю об обработке оши- бок… Нам еще предстоит большая работа .
    Прежде всего хотелось бы избавиться от конструкции выбора в setArgument
    [G23] .
    В идеале она должна быть заменена единственным вызовом
    ArgumentMarshaler.set
    Это означает, что код setIntArg
    , setStringArg и setBooleanArg должен быть пере-
    265

    266
    Глава 14 . Последовательное очищение мещен в соответствующие классы, производные от
    ArgumentMarshaler
    . Однако при этом возникает одна проблема .
    Внимательно присмотревшись к функции setIntArg
    , можно заметить, что в ней используются две переменные экземпляров: args и currentArg
    . Чтобы переместить setIntArg в
    BooleanArgumentMarshaler
    , мне придется передать args и currentArgs в аргументах при вызове . Решение получается «грязным» [F1] . Я бы предпочел передать один аргумент вместо двух . К счастью, у проблемы существует простое решение: мы можем преобразовать массив args в list и передать
    Iterator функ- циям set
    . Следующее преобразование было проведено за десять шагов, с обяза- тельным выполнением всех тестов после каждого шага . Здесь я приведу только конечный результат, но вы легко сможете опознать большинство промежуточных шагов по этому листингу .
    public class Args {
    private String schema;
    private String[] args;
    private boolean valid = true;
    private Set unexpectedArguments = new TreeSet();
    private Map marshalers = new HashMap();
    private Set argsFound = new HashSet();
    private
    Iterator currentArgument;
    private char errorArgumentId = '\0';
    private String errorParameter = "TILT";
    private ErrorCode errorCode = ErrorCode.OK;
    private List argsList;
    private enum ErrorCode {
    OK, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT}
    public Args(String schema, String[] args) throws ParseException {
    this.schema = schema;
    argsList = Arrays.asList(args);
    valid = parse();
    }
    private boolean parse() throws ParseException {
    if (schema.length() == 0 &&
    argsList.size() == 0)
    return true;
    parseSchema();
    try {
    parseArguments();
    } catch (ArgsException e) {
    }
    return valid;
    }
    --- private boolean parseArguments() throws ArgsException {
    for (currentArgument =
    argsList.iterator(); currentArgument.hasNext();) {
    String arg = currentArgument.
    next();
    266

    Аргументы String
    267
    parseArgument(arg);
    }
    return true;
    }
    --- private void setIntArg(ArgumentMarshaler m) throws ArgsException {
    String parameter = null;
    try {
    parameter = currentArgument.
    next();
    m.set(parameter);
    } catch (
    NoSuchElementException e) {
    errorCode = ErrorCode.MISSING_INTEGER;
    throw new ArgsException();
    } catch (ArgsException e) {
    errorParameter = parameter;
    errorCode = ErrorCode.INVALID_INTEGER;
    throw e;
    }
    }
    private void setStringArg(ArgumentMarshaler m) throws ArgsException {
    try {
    m.set(currentArgument.
    next());
    } catch (
    NoSuchElementException e) {
    errorCode = ErrorCode.MISSING_STRING;
    throw new ArgsException();
    }
    }
    Все изменения были простыми и не нарушали работы тестов . Теперь можно за- няться перемещением функций в соответствующие производные классы . Начнем с внесения изменений в setArgument
    :
    private boolean setArgument(char argChar) throws ArgsException {
    ArgumentMarshaler m = marshalers.get(argChar);
    if (m == null)
    return false;
    try {
    if (m instanceof BooleanArgumentMarshaler)
    setBooleanArg(m);
    else if (m instanceof StringArgumentMarshaler)
    setStringArg(m);
    else if (m instanceof IntegerArgumentMarshaler)
    setIntArg(m);
    else return false;
    } catch (ArgsException e) {
    valid = false;
    errorArgumentId = argChar;
    throw e;
    }
    return true;
    }
    267

    268
    Глава 14 . Последовательное очищение
    Это изменение важно, потому что мы хотим полностью устранить цепочку if
    - else
    . Для этого из нее необходимо вывести состояние ошибки .
    Теперь можно переходить к перемещению функций set
    . Функция setBooleanArg тривиальна, поэтому начнем с нее . Наша задача — изменить функцию setBool- eanArg так, чтобы она просто передавала управление
    BooleanArgumentMarshaler private boolean setArgument(char argChar) throws ArgsException {
    ArgumentMarshaler m = marshalers.get(argChar);
    if (m == null)
    return false;
    try {
    if (m instanceof BooleanArgumentMarshaler)
    setBooleanArg(m,
    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;
    }
    --- 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
    268

    Аргументы String
    269
    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();
    }
    }
    269

    270
    Глава 14 . Последовательное очищение 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 {
    270

    Аргументы String
    271
    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;
    }
    }
    public void set(String s) throws ArgsException {
    try {
    intValue = Integer.parseInt(s);
    } catch (NumberFormatException e) {
    throw new ArgsException();
    }
    }
    public Object get() {
    return intValue;
    }
    }
    А теперь завершающий штрих: убираем цепочку if
    - else
    !
    private boolean setArgument(char argChar) throws ArgsException {
    ArgumentMarshaler m = marshalers.get(argChar);
    if (m == null)
    return false;
    try {
    271

    272
    Глава 14 . Последовательное очищение m.set(currentArgument);
    return true;
    } catch (ArgsException e) {
    valid = false;
    errorArgumentId = argChar;
    throw e;
    }
    }
    Избавляемся от лишних функций в
    IntegerArgumentMarshaler и слегка чистим код:
    private class IntegerArgumentMarshaler extends ArgumentMarshaler {
    private int intValue = 0
    public void set(Iterator currentArgument) throws ArgsException {
    String parameter = null;
    try {
    parameter = currentArgument.next();
    1   ...   26   27   28   29   30   31   32   33   ...   49


    написать администратору сайта