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

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


Скачать 3.16 Mb.
НазваниеСоздание, анализ ирефакторинг
Дата29.09.2022
Размер3.16 Mb.
Формат файлаpdf
Имя файлаChistyj_kod_-_Sozdanie_analiz_i_refaktoring_(2013).pdf
ТипКнига
#706087
страница27 из 49
1   ...   23   24   25   26   27   28   29   30   ...   49
листинг 14 .3 . ArgumentMarshaler.java public interface ArgumentMarshaler {
void set(Iterator currentArgument) throws ArgsException;
}
листинг 14 .4 . BooleanArgumentMarshaler.java public class BooleanArgumentMarshaler implements ArgumentMarshaler {
private boolean booleanValue = false;
public void set(Iterator currentArgument) throws ArgsException {
booleanValue = true;
}
public static boolean getValue(ArgumentMarshaler am) {
if (am != null && am instanceof BooleanArgumentMarshaler)
return ((BooleanArgumentMarshaler) am).booleanValue;
else return false;
}
}
листинг 14 .5 . StringArgumentMarshaler.java import static com.objectmentor.utilities.args.ArgsException.ErrorCode.*;
public class StringArgumentMarshaler implements ArgumentMarshaler {
private String stringValue = "";
public void set(Iterator currentArgument) throws ArgsException {
try {
stringValue = currentArgument.next();
} catch (NoSuchElementException e) {
throw new ArgsException(MISSING_STRING);
}
}
public static String getValue(ArgumentMarshaler am) {
if (am != null && am instanceof StringArgumentMarshaler)
продолжение
229

230
Глава 14 . Последовательное очищение
листинг 14 .5 . (продолжение)
return ((StringArgumentMarshaler) am).stringValue;
else return "";
}
}
листинг 14 .6 . IntegerArgumentMarshaler.java import static com.objectmentor.utilities.args.ArgsException.ErrorCode.*;
public 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) {
throw new ArgsException(MISSING_INTEGER);
} catch (NumberFormatException e) {
throw new ArgsException(INVALID_INTEGER, parameter);
}
}
public static int getValue(ArgumentMarshaler am) {
if (am != null && am instanceof IntegerArgumentMarshaler)
return ((IntegerArgumentMarshaler) am).intValue;
else return 0;
}
}
Другие классы, производные от
ArgumentMarshaler
, строятся по тому же шаблону, что и классы для массивов double и
String
. Здесь они не приводятся для экономии места . Оставляю их вам для самостоятельной работы .
Возможно, вы заметили еще одно обстоятельство: где определяются константы для кодов ошибок? Они находятся в классе
ArgsException
(листинг 14 .7) .
листинг 14 .7 . ArgsException.java import static com.objectmentor.utilities.args.ArgsException.ErrorCode.*;
public class ArgsException extends Exception {
private char errorArgumentId = '\0';
private String errorParameter = null;
private ErrorCode errorCode = OK;
public ArgsException() {}
public ArgsException(String message) {super(message);}
230

Реализация Args
231
public ArgsException(ErrorCode errorCode) {
this.errorCode = errorCode;
}
public ArgsException(ErrorCode errorCode, String errorParameter) {
this.errorCode = errorCode;
this.errorParameter = errorParameter;
}
public ArgsException(ErrorCode errorCode, char errorArgumentId, String errorParameter) {
this.errorCode = errorCode;
this.errorParameter = errorParameter;
this.errorArgumentId = errorArgumentId;
}
public char getErrorArgumentId() {
return errorArgumentId;
}
public void setErrorArgumentId(char errorArgumentId) {
this.errorArgumentId = errorArgumentId;
}
public String getErrorParameter() {
return errorParameter;
}
public void setErrorParameter(String errorParameter) {
this.errorParameter = errorParameter;
}
public ErrorCode getErrorCode() {
return errorCode;
}
public void setErrorCode(ErrorCode errorCode) {
this.errorCode = errorCode;
}
public String errorMessage() {
switch (errorCode) {
case OK:
return "TILT: Should not get here.";
case UNEXPECTED_ARGUMENT:
return String.format("Argument -%c unexpected.", errorArgumentId);
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);
продолжение
231

232
Глава 14 . Последовательное очищение
листинг 14 .7 (продолжение)
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);
case INVALID_ARGUMENT_NAME:
return String.format("'%c' is not a valid argument name.", errorArgumentId);
case INVALID_ARGUMENT_FORMAT:
return String.format("'%s' is not a valid argument format.", errorParameter);
}
return "";
}
public enum ErrorCode {
OK, INVALID_ARGUMENT_FORMAT, UNEXPECTED_ARGUMENT, INVALID_ARGUMENT_NAME,
MISSING_STRING,
MISSING_INTEGER, INVALID_INTEGER,
MISSING_DOUBLE, INVALID_DOUBLE}
}
Удивительно, какой объем кода понадобился для воплощения всех подробностей этой простой концепции . Одна из причин заключается в том, что мы используем весьма «многословный» язык . Поскольку Java относится к числу языков со ста- тической типизацией, для удовлетворения требований системы типов в нем ис- пользуется немалый объем кода . На таких языках, как Ruby, Python или Smalltalk, программа получится гораздо короче
1
Пожалуйста, перечитайте код еще раз . Обратите особое внимание на выбор имен, размеры функций и форматирование кода . Возможно, опытные програм- мисты найдут отдельные недочеты в стиле или структуре кода . Но я надеюсь, что в целом вы согласитесь с тем, что код хорошо написан, а его структура чиста и логична .
Скажем, после чтения кода вам должно быть очевидно, как добавить поддерж- ку нового типа аргументов (например, дат или комплексных чисел), и это по- требует относительно небольших усилий с вашей стороны . Для этого доста- точно создать новый класс, производный от
ArgumentMarshaler
, новую функцию getXXX
и включить новое условие case в функцию parseSchemaElement
. Вероятно, также потребуется новое значение
ArgsException.ErrorCode и новое сообщение об ошибке .
1
Недавно я переписал этот модуль на Ruby . Код занимает в 7 раз меньше места и имеет более качественную структуру .
232

Args: черновик
233
Как я это сделал?
Позвольте вас успокоить: я не написал эту программу от начала до конца в ее те- кущем виде . Более того, я не ожидаю, что вы сможете писать чистые и элегантные программы за один проход . Если мы чему-то и научились за последнюю пару десятилетий, так это тому, что программирование ближе к ремеслу, чем к науке .
Чтобы написать чистый код, мы сначала пишем грязный код, а затем очищаем его .
Вряд ли вас это удивит . Мы усвоили эту истину еще в начальной школе, когда учителя заставляли нас (обычно безуспешно) писать планы сочинений . Пред- полагалось, что мы должны сначала написать первый вариант плана, затем вто- рой, потом еще несколько версий, пока не придем к окончательной версии . Они пытались объяснить нам, что четкое и ясное сочинение появляется в результате последовательного усовершенствования .
Многие начинающие программисты (впрочем, как и большинство школьников, пишущих сочинения) не слишком усердно следуют этому совету . Они считают, что их главная цель — заставить программу работать . Когда программа «зарабо- тает», они переходят к следующей задаче, оставляя «работающую» программу в том состоянии, в котором она «заработала» . Опытные программисты знают, что с профессиональной точки зрения такой подход равносилен самоубийству .
args: черновик
В листинге 14 .8 приведена более ранняя версия класса
Args
. Она «работает» .
И при этом выглядит крайне неряшливо .
листинг 14 .8 . Args.java (первая версия)
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 booleanArgs = new HashMap();
private Map stringArgs = new HashMap();
private Map intArgs = 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}
продолжение
233

234
Глава 14 . Последовательное очищение
листинг 14 .8 (продолжение)
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;
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))
parseBooleanSchemaElement(elementId);
else if (isStringSchemaElement(elementTail))
parseStringSchemaElement(elementId);
else if (isIntegerSchemaElement(elementTail)) {
parseIntegerSchemaElement(elementId);
} 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);
}
}
234

Args: черновик
235
private void parseBooleanSchemaElement(char elementId) {
booleanArgs.put(elementId, false);
}
private void parseIntegerSchemaElement(char elementId) {
intArgs.put(elementId, 0);
}
private void parseStringSchemaElement(char elementId) {
stringArgs.put(elementId, "");
}
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("#");
}
private boolean parseArguments() throws ArgsException {
for (currentArgument = 0; currentArgument < args.length; 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;
}
}
продолжение
235

236
Глава 14 . Последовательное очищение
листинг 14 .8 (продолжение)
private boolean setArgument(char argChar) throws ArgsException {
if (isBooleanArg(argChar))
setBooleanArg(argChar, true);
else if (isStringArg(argChar))
setStringArg(argChar);
else if (isIntArg(argChar))
setIntArg(argChar);
else return false;
return true;
}
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.put(argChar, new Integer(parameter));
} catch (ArrayIndexOutOfBoundsException e) {
valid = false;
errorArgumentId = argChar;
errorCode = ErrorCode.MISSING_INTEGER;
throw new ArgsException();
} catch (NumberFormatException e) {
valid = false;
errorArgumentId = argChar;
errorParameter = parameter;
errorCode = ErrorCode.INVALID_INTEGER;
throw new ArgsException();
}
}
private void setStringArg(char argChar) throws ArgsException {
currentArgument++;
try {
stringArgs.put(argChar, args[currentArgument]);
} catch (ArrayIndexOutOfBoundsException e) {
valid = false;
errorArgumentId = argChar;
errorCode = ErrorCode.MISSING_STRING;
throw new ArgsException();
}
}
private boolean isStringArg(char argChar) {
return stringArgs.containsKey(argChar);
}
236

Args: черновик
237
private void setBooleanArg(char argChar, boolean value) {
booleanArgs.put(argChar, value);
}
private boolean isBooleanArg(char argChar) {
return booleanArgs.containsKey(argChar);
}
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);
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();
}
private boolean falseIfNull(Boolean b) {
return b != null && b;
}
продолжение
237

238
Глава 14 . Последовательное очищение
листинг 14 .8 (продолжение)
private int zeroIfNull(Integer i) {
return i == null ? 0 : i;
}
private String blankIfNull(String s) {
return s == null ? "" : s;
}
public String getString(char arg) {
return blankIfNull(stringArgs.get(arg));
}
public int getInt(char arg) {
return zeroIfNull(intArgs.get(arg));
}
public boolean getBoolean(char arg) {
return falseIfNull(booleanArgs.get(arg));
}
public boolean has(char arg) {
return argsFound.contains(arg);
}
public boolean isValid() {
return valid;
}
private class ArgsException extends Exception {
}
}
Надеюсь, при виде этой глыбы кода вам захотелось сказать: «Как хорошо, что она не осталась в таком виде!» Если вы почувствовали нечто подобное, вспомните, что другие люди чувствуют то же самое при виде вашего кода, оставшегося на стадии «черновика» .
Вообще говоря, «черновик» — самое мягкое, что можно сказать об этом коде . Оче- видно, что перед нами незавершенная работа . От одного количества переменных экземпляров можно прийти в ужас . Загадочные строки вроде "TILT”
, контейнеры
HashSet и
TreeSet
, конструкции try
- catch
-
catch только увеличивают масштабы этого беспорядочного месива .
Я вовсе не собирался писать беспорядочное месиво . В самом деле, я постарался сохранить более или менее разумную организацию кода . Об этом свидетельствует хотя бы выбор имен функций и переменных, а также наличие у программы при- митивной структуры . Но совершенно очевидно, что проблемы вышли из-под моего контроля .
238

Args: черновик
239
Неразбериха накапливалась постепенно . Ранние версии выглядели вовсе не так отвратительно . Для примера в листинге 14 .9 приведена начальная версия, под- держивающая только логические аргументы .
листинг 14 .9 . Args.java (только Boolean)
package com.objectmentor.utilities.getopts;
import java.util.*;
public class Args {
private String schema;
private String[] args;
private boolean valid;
private Set unexpectedArguments = new TreeSet();
private Map booleanArgs = new HashMap();
private int numberOfArguments = 0;
public Args(String schema, String[] args) {
this.schema = schema;
this.args = args;
valid = parse();
}
public boolean isValid() {
return valid;
}
private boolean parse() {
if (schema.length() == 0 && args.length == 0)
return true;
parseSchema();
parseArguments();
return unexpectedArguments.size() == 0;
}
private boolean parseSchema() {
for (String element : schema.split(",")) {
parseSchemaElement(element);
}
return true;
}
private void parseSchemaElement(String element) {
if (element.length() == 1) {
parseBooleanSchemaElement(element);
}
}
продолжение
239

240
Глава 14 . Последовательное очищение
1   ...   23   24   25   26   27   28   29   30   ...   49


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