#java #design-patterns
#java #шаблоны проектирования
Вопрос:
У меня есть класс, который имеет один или несколько входных списков, и ему необходимо обрабатывать и обновлять другие списки, область действия которых находится за пределами класса. Какой может быть хороший шаблон для использования здесь? Я попробовал следующие два шаблона здесь:
- Обновление напрямую с помощью pass-by-reference
- Использование обратных вызовов, чтобы позволить вызывающей стороне обрабатывать то, как они хотят справиться с обновлением
Примеры
- Использование прямого обновления
public class SomeClass {
public void process(List<String> inputList, List<StringContainer> errorContainerList, List<String> validList) {
for (String str : inputList) {
if (someCondition) {
validList.add(str);
} else {
errorContainerList.add(new StringContainer(str, new SomeError()));
}
}
}
}
void someCaller(List<String> inputList) {
List<String> validList = new ArrayList<>();
List<StringContainer> errorContainerList = new ArrayList<>();
SomeClass obj = new SomeClass();
obj.process(inputList, errorContainerList, validList);
// Similarly,
validList.remove(...);
errorContainerList.add(...);
}
- Использование обратных вызовов
public interface StringErrorHandler {
void handle(String str, SomeError error);
}
public interface ValidInputHandler {
void handle(String str);
}
public class SomeClass {
public void process(List<String> inputList, StringErrorHandler errorHandler, ValidInputHandler validInputHandler) {
for (String str : inputList) {
if (someCondition) {
validInputHandler.handle(str);
} else {
errorHandler.handle(str, new SomeError());
}
}
}
}
void someCaller(List<String> inputList) {
List<String> validList = new ArrayList<>();
List<StringContainer> errorContainerList = new ArrayList<>();
SomeClass obj = new SomeClass();
obj.process(inputList, new StringErrorHandler() {
@Override
public void handle(String str, SomeError error) {
errorContainerList.add(new StringContainer(str, error));
}
}, new ValidInputHandler() {
@Override
public void handle(String str) {
validList.add(str);
}
});
// Similarly,
validList.remove(...);
errorContainerList.add(...);
}
Комментарии:
1. Что вы подразумеваете под «хорошим» шаблоном? Что делает шаблон «хорошим»?
2. В чем проблема с выполнением любого из ваших способов? Есть ли исключение?
3. @Sweeper Поскольку подобный случай встречается очень часто, мне интересно, существуют ли какие-либо ранее существовавшие шаблоны, о которых я не знаю.
4. @NomadMaker Здесь проблема с 1-м подходом заключается в том, что если количество входных и выходных данных увеличивается, то увеличивается количество параметров для метода, что может усложнить рефакторинг кода. Для 2-го подхода мне интересно, не приведет ли это к путанице в отладке.
Ответ №1:
Я бы не использовал ни один из них. Они оба излишне сложны. Давайте подумаем, в чем ваша реальная проблема здесь? Если бы вы не заботились об обработке ошибок, вы, вероятно, написали бы что-то вроде этого:
public List<String> process(List<String> inputList) {
List<String> validList = new ArrayList<>();
for (String str : inputList) {
if (someCondition) {
validList.add(str);
}
}
return validList;
}
Итак, проблема в том, что вы не можете вернуть два значения из метода в Java. Но чтобы справиться с этим, вы можете добавить простой вложенный класс, имеющий эти 2 значения, и затем клиент может легко их извлечь:
public ProcessResult process(List<String> inputList) {
List<StringContainer> errorContainerList = new ArrayList<>();
List<String> validList = new ArrayList<>();
for (String str : inputList) {
if (someCondition) {
validList.add(str);
} else {
errorContainerList.add(new StringContainer(str, new SomeError()));
}
}
return new ProcessResult(validList, errorContainerList)
}
public static class ProcessResult{
public final List<String> validList;
public final List<StringContainer> errorContainerList;
public ProcessResult(List<String> validList, List<StringContainer> errorContainerList) {
this.validList = validList;
this.errorContainerList = errorContainerList;
}
}
void someCaller(List<String> inputList) {
List<String> validList = new ArrayList<>();
List<StringContainer> errorContainerList = new ArrayList<>();
SomeClass obj = new SomeClass();
ProcessResult pr=obj.process(inputList);
validList.addAll(pr.validList);
errorContainerList.addAll(pr.errorContainerList);
}
Это лучше по нескольким причинам, но наиболее важным является то, что у вашего метода нет побочных эффектов и что результат метода возвращается явно, что мы и должны делать в java
Комментарии:
1. В случае частых вызовов это решение создаст много временных переменных (ProcesResult). Как вы думаете, это вызовет какие-либо проблемы с GC?
2. Java действительно хороша в работе с избыточными объектами. В 99,999% случаев вам не нужно думать об этом, пока у вас не возникнут реальные проблемы с производительностью (проверьте преждевременную оптимизацию).