Перевод статьи Immutable Setters: Naming Conventions

Сеттер поля для иммутабельного объекта отличается от обычного сеттера. Так как не изменяет объект, а создает новый с измененым параметром класса. Это веская причина для изменения нейминга методов.

В этой статье список различных возможных соглашений для иммутабельных классов, с примерами на Java.

withX(…)

Самый распространенный в настоящее время шаблон для сеттеров. Например, это наименование используется по умолчанию для генерируемых сеттеров в фреймворке Immutables. Пример:

Foo newFoo = foo.withX(1047);

В Immutables возможно изменить шаблон наименования через аннотацию @Value.Style, опция называется with="...", лишний раз подчеркивает with как соглашение по-умолчанию.

Соглашение использовать with широко распространено, примеры сеттеров вы сможете найти в библиотеках Guavа и Java Time.

Just x(…)

Другой подход, заключается в полном отсутствии префикса. Пример использования найдете также в Immutables при сборке объекта, используя паттерн Builder. Пример:

Foo foo = ImmutableFoo.builder()
                      .x(1047)
                      .y("Hello World")
                      .build();

Если используете такой вариант наименования, то посмотрите также и вариант геттера в таком же стиле. Это будет перегруженный метод без аргументов, даже если не используется паттер Builder в классе. Пример:

Foo newFoo = foo.x(5);  // setter - один аргумент
int x = newFoo.x();     // getter - без аргументов

Такое соглашение использует фреймворк Java Spark.

setX(…)

Некоторые API используют стандартные наименования сеттеров для иммутабельных классов. Такие имена методов неочевидны, так обращаясь к объекту мы не может сразу отличить иммутабельность, а для нового участника проекта это будет каждый раз сюрприз. При этом и в стандартном JDK такое подход также использутся.

Работает с объектом типа BigInteger и пишем код для установки значения:

bigInt.setBit(2);

…и это пример ошибки, так как такой метод не изменяет состояние объекта, а создает новый. При использовании сеттера в таком виде возвращаемый объект не используется, хотя и создается. Правильно использовать сеттер:

BigInteger newBigInt = bigInt.setBit(2);

deriveX(…)

Для подчеркивания факта создания нового объекта (derived - производный), вы можете использовать шаблон deriveX(…). Иммутабельный класс Font в Java API следует этому правилу. Если вы хотите создать новый шрифт, например, с указанием размера, используйте:

Font newFont = font.deriveFont(newSize);

Класс Font используется с незапамятных времен, а метод deriveFont(...) c JDK 1.2. В настоящее время данный формат нейминга не рапространен.

Иммутабельный объект, как аргумент

Когда иммутабельный объект является аргументом метода и влияет на вызываемый объект, то сеттер не является сеттером по сути. В этом случае не требуется префикса вовсе. Например, умножение чисел:

BigDecimal newBigDec = bigDec.multiply(BigDecimal.TEN);

…сигнатура похожа не сеттер, при этом имя метода multiply точно описывает действие и данный нейминг в этом случае подходит лучше других альтернатив.

Аналогично String.substring, Path.resolve и не только.