Главная страница

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


Скачать 3.16 Mb.
НазваниеСоздание, анализ ирефакторинг
Дата29.09.2022
Размер3.16 Mb.
Формат файлаpdf
Имя файлаChistyj_kod_-_Sozdanie_analiz_i_refaktoring_(2013).pdf
ТипКнига
#706087
страница29 из 49
1   ...   25   26   27   28   29   30   31   32   ...   49
public void setInteger(int i) {
integerValue = i;
}
public int getInteger() {
return integerValue;
}
}
Переместив всю логику компоновки аргументов в
ArgumentMarshaler
, я занялся перемещением функциональности в производные классы . На первом этапе я должен был переместить функцию setBoolean в
BooleanArgumentMarshaller и по- заботиться о том, чтобы она правильно вызывалась . Для этого был создан аб- страктный метод set private
abstract class ArgumentMarshaler {
protected boolean booleanValue = false;
private String stringValue;
private int integerValue;
public void setBoolean(boolean value) {
booleanValue = value;
}
public boolean getBoolean() {
250

Аргументы String
251
return booleanValue;
}
public void setString(String s) {
stringValue = s;
}
public String getString() {
return stringValue == null ? "" : stringValue;
}
public void setInteger(int i) {
integerValue = i;
}
public int getInteger() {
return integerValue;
}
public abstract void set(String s);
}
Затем метод set был реализован в
BooleanArgumentMarshaller private class BooleanArgumentMarshaler extends ArgumentMarshaler {
public void set(String s) {
booleanValue = true;
}
}
Наконец, вызов setBoolean был заменен вызовом set private void setBooleanArg(char argChar, boolean value) {
booleanArgs.get(argChar).
set("true");
}
Все тесты прошли успешно . Так как изменения привели к перемещению set в
BooleanArgumentMarshaler
, я удалил метод setBoolean из базового класса
ArgumentMarshaler
Обратите внимание: абстрактная функция set получает аргумент
String
, но реализация в классе
BooleanArgumentMarshaller его не использует . Я доба- вил этот аргумент, потому что знал, что он будет использоваться классами
StringArgumentMarshaller и
IntegerArgumentMarshaller
На следующем шаге я решил разместить метод get в
BooleanArgumentMarshaler
Подобные размещения get всегда выглядят уродливо, потому что фактически возвращается тип
Object
, который в данном случае приходится преобразовывать в
Boolean public boolean getBoolean(char arg) {
Args.ArgumentMarshaler am = booleanArgs.get(arg);
return am != null && (
Boolean)am.get();
}
251

252
Глава 14 . Последовательное очищение
Просто для того, чтобы программа компилировалась, я добавил в
ArgumentMar- shaler функцию get private abstract class ArgumentMarshaler {
public Object get() {
return null;
}
}
Программа компилировалась, а тесты, разумеется, не проходили . Чтобы тесты снова заработали, достаточно объявить метод get абстрактным и реализовать его в
BooleanAgumentMarshaler private abstract class ArgumentMarshaler {
protected boolean booleanValue = false;
public
abstract Object get();
}
private class BooleanArgumentMarshaler extends ArgumentMarshaler {
public void set(String s) {
booleanValue = true;
}
public Object get() {
return booleanValue;
}
}
Итак, тесты снова проходят успешно . Теперь оба метода get и set размещаются в
BooleanArgumentMarshaler
! Это позволило мне удалить старую функцию get-
Boolean из
ArgumentMarshaler
, переместить защищенную переменную booleanValue в
BooleanArgumentMarshaler и объявить ее приватной .
Аналогичные изменения были внесены для типа
String
. Я реализовал методы set и get
, удалил ненужные функции и переместил переменные .
private void setStringArg(char argChar) throws ArgsException {
currentArgument++;
try {
stringArgs.get(argChar).
set(args[currentArgument]);
} catch (ArrayIndexOutOfBoundsException e) {
valid = false;
errorArgumentId = argChar;
errorCode = ErrorCode.MISSING_STRING;
throw new ArgsException();
}
}
public String getString(char arg) {
Args.ArgumentMarshaler am = stringArgs.get(arg);
return am == null ? "" : (
String) am.get();
}
252

Аргументы String
253
private abstract class ArgumentMarshaler {
private int integerValue;
public void setInteger(int i) {
integerValue = i;
}
public int getInteger() {
return integerValue;
}
public abstract void set(String s);
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 {
public void set(String s) {
}
public Object get() {
return null;
}
}
}
253

254
Глава 14 . Последовательное очищение
Осталось лишь повторить этот процесс для integer
. На этот раз задача немного усложняется: целые числа необходимо разбирать, а в ходе разбора возможны исключения . Но внешний вид кода улучшается тем, что вся концепция
Number-
FormatException скрыта в классе
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) {
254

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

256
Глава 14 . Последовательное очищение
return m instanceof BooleanArgumentMarshaler;
}
Тесты по-прежнему проходят . Я внес аналогичные изменения в isIntArg и is-
StringArg 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;
}
private boolean isIntArg(
ArgumentMarshaler m) {
return m instanceof IntegerArgumentMarshaler;
}
private boolean isStringArg(
ArgumentMarshaler m) {
return m instanceof StringArgumentMarshaler;
}
private boolean isBooleanArg(
ArgumentMarshaler m) {
return m instanceof BooleanArgumentMarshaler;
}
Причин для существования трех методов isxxxArg не осталось . Я оформил их в виде встроенного кода:
private boolean setArgument(char argChar) throws ArgsException {
ArgumentMarshaler m = marshalers.get(argChar);
if (
m instanceof BooleanArgumentMarshaler)
setBooleanArg(argChar);
else if (
m instanceof StringArgumentMarshaler)
setStringArg(argChar);
else if (
m instanceof IntegerArgumentMarshaler)
setIntArg(argChar);
else
256

Аргументы String
257
return false;
return true;
}
На следующем шаге я перешел на использование ассоциативного массива mar- shalers в функциях set
, отказываясь от использования трех старых контейнеров .
Преобразование началось с
Boolean
:
private boolean setArgument(char argChar) throws ArgsException {
ArgumentMarshaler m = marshalers.get(argChar);
if (m instanceof BooleanArgumentMarshaler)
setBooleanArg(
m);
else if (m instanceof StringArgumentMarshaler)
setStringArg(argChar);
else if (m instanceof IntegerArgumentMarshaler)
setIntArg(argChar);
else return false;
return true;
}
private void setBooleanArg(
ArgumentMarshaler m) {
try {
m.set(“true”); // было: booleanArgs.get(argChar).set(«true»);
} catch (ArgsException e) {
}
}
Тесты проходили успешно, и я сделал то же самое для типов
String и
Integer
. Это позволило мне интегрировать часть некрасивого кода обработки исключений в функцию setArgument 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++;
257

258
Глава 14 . Последовательное очищение
String parameter = null;
try {
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();
}
}
Я вплотную подошел к удалению трех старых объектов
Map
. Прежде всего было необходимо привести функцию getBoolean
:
public boolean getBoolean(char arg) {
Args.ArgumentMarshaler am = booleanArgs.get(arg);
return am != null && (Boolean) am.get();
}
к следующему виду:
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;
}
Возможно, последнее изменение вас удивило . Почему я вдруг решил обраба- тывать
ClassCastException
? Дело в том, что наряду с набором модульных тестов у меня был отдельный набор приемочных тестов, написанных для FitNesse . Ока- залось, что тесты FitNesse проверяли, что при вызове getBoolean для аргумента с типом, отличным от
Boolean
, возвращается false
. Модульные тесты этого не делали . До этого момента я запускал только модульные тесты
1 1
Чтобы предотвратить подобные сюрпризы в будущем, я добавил новый модульный тест, который запускал все тесты FitNesse .
258

Аргументы String
259
Последнее изменение позволило исключить еще одну точку использования объ- екта
Map для типа
Boolean
:
private void parseBooleanSchemaElement(char elementId) {
ArgumentMarshaler m = new BooleanArgumentMarshaler();
booleanArgs.put(elementId, m);
marshalers.put(elementId, m);
}
Теперь объект
Map для типа
Boolean можно было удалить:
public class Args {
private Map booleanArgs = new HashMap();
private Map stringArgs = new HashMap();
private Map intArgs = new HashMap();
private Map marshalers = new HashMap();
Далее я проделал аналогичную процедуру для аргументов
String и
Integer и не- много подчистил код: private void parseBooleanSchemaElement(char elementId) {
marshalers.put(elementId,
new BooleanArgumentMarshaler());
}
private void parseIntegerSchemaElement(char elementId) {
marshalers.put(elementId,
new IntegerArgumentMarshaler());
}
private void parseStringSchemaElement(char elementId) {
marshalers.put(elementId,
new StringArgumentMarshaler());
}
public String getString(char arg) {
Args.ArgumentMarshaler am =
marshalers.get(arg);
try {
return am == null ? "" : (String) am.get();
} catch (ClassCastException e) {
return "";
}
}
public class Args {
private Map stringArgs = new HashMap();
private Map intArgs = new HashMap();
private Map marshalers = new HashMap();
259

260
Глава 14 . Последовательное очищение
Затем я подставил в parseSchemaElement код трех методов parse
, сократившихся до одной команды:
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);
}
}
Давайте взглянем на общую картину . В листинге 14 .12 представлена текущая форма класса
Args
листинг 14 .12 . Args.java (после первой переработки)
package com.objectmentor.utilities.getopts;
import java.text.ParseException;
import java.util.*;
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 int currentArgument;
private char errorArgumentId = '\0';
private String errorParameter = "TILT";
private ErrorCode errorCode = ErrorCode.OK;
private enum ErrorCode {
OK, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT}
public Args(String schema, String[] args) throws ParseException {
this.schema = schema;
this.args = args;
valid = parse();
}
private boolean parse() throws ParseException {
if (schema.length() == 0 && args.length == 0)
return true;
260

Аргументы String
1   ...   25   26   27   28   29   30   31   32   ...   49


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