The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
Предварительная обработка аргументов конструкторов перед их инкапсуляцией кажется плохой практикой. Однако часто необходимо именно это: выполнять некоторые манипуляции с объектами, предоставленными в качестве аргументов, и только после этого присваивать их атрибутам созданного объекта. Для этой цели я предлагаю использовать предварительные функции (prestructors), которые могут быть методами или самостоятельными объектами.
Предположим, это ваш код:
import java.util.List;
import java.util.Collections;
class Books {
private final List<String> titles;
Books(List<String> list) {
this.titles = Collections.unmodifiableList(list);
}
}
Единственный конструктор ожидает список заголовков, который инкапсулируется как this.titles
для использования в будущем. Он также защищен от случайных изменений с помощью декоратора JDK unmodifiableList
. Пока все хорошо. Теперь мы хотим сделать наш класс немного умнее и позволить ему принимать не только List
, но и массив строк:
class Books {
private List<String> titles;
Books(List<String> list) {
this.titles = Collections.unmodifiableList(list);
}
Books(String... array) {
final List<String> list = new ArrayList<>(array.length);
for (final String title : array) {
list.add(title);
}
this.titles = list;
}
}
Что не так с этим кодом? Те из вас, кто читал мои предыдущие посты в блоге об ООП, определенно знают ответ. Во-первых, здесь есть два основных конструктора, что является еще одной плохой практикой. Во-вторых, во втором конструкторе присутствует код, что также является плохой идеей.
Вот как я обычно рефакторю этот код, чтобы решить оба упомянутых проблемы:
class Books {
private List<String> titles;
Books(List<String> list) {
this.titles = Collections.unmodifiableList(list);
}
Books(String... array) {
this(Books.toList(array));
}
private static List<String> toList(String... array) {
final List<String> list = new ArrayList<>(array.length);
for (final String title : array) {
list.add(title);
}
return list;
}
}
Я называю этот новый статический метод toList()
преструктором: он используется только в момент создания объекта и только из вторичного конструктора.
Еще лучшим способом было бы создать новый класс ToList
, который делал бы точно то же самое, но более декларативным и ленивым способом:
class Books {
private List<String> titles;
Books(List<String> list) {
this.titles = Collections.unmodifiableList(list);
}
Books(String... array) {
this(new ToList(array));
}
}
class ToList<T> implements List<T> {
private final T[] array;
ToList(T... items) {
this.array = items;
}
// All required methods of the List interface
}
ListOf
из Cactoos - это отличный пример такого предварительного конструктора.
Translated by ChatGPT gpt-3.5-turbo/36 on 2023-10-12 at 16:02