Сеттер-паттерн - один из самых популярных во многих языках программирования. Инженеры используют его так давно, что такой подход стал очевиден. Даже IDE теперь может генерировать сеттеры для вас, просто нажав несколько горячих клавиш. Я думаю, что использование сеттеров - единственное, чего следует избегать в любом случае. Эта запись содержит фрагменты кода на Java, но эти правила могут быть применены к любому другому языку.

Бьюсь об заклад, что все видели что-то подобное.

Этот подход кажется довольно элегантным, но давайте подумаем. Во-первых, мы создаем пустой объект, внутри которого ничего нет. После этого шаг за шагом присваиваем требуемые значения. Что, если мы забыли указать имя или возраст? Это означает, что будущие исполнения будут обрабатывать неполный объект. Более того, наличие сеттеров означает, что объект является изменяемым и может быть изменен кем угодно в любое время. Посмотрим еще на один пример.

Мы не ожидаем каких-либо мутаций в manager при вызове findAllSubordinates, но как мы можем это узнать? В этом случае humanResources может быть просто интерфейсом. Мы не знаем, как именно это работает. Итак, мы не знаем manager будущее состояние, потому что его общедоступный API содержит изменяющиеся методы, которые могут быть вызваны.

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

И на выходе

Map: {[name=Jack, age=23]=35, [name=Julia, age=35]=65}
Corrupted map: {[name=Jack, age=55]=35, [name=Julia, age=35]=65}
Jack's rate: null
Julia's rate: 65

Это происходит потому, что новый хэш-код Джека не совпадает с тем, который указан на карте. Итак, ключи считаются разными. Есть хак, который может исправить эту ситуацию, но он заслуживает отдельного рассказа.

Изменить: Person должен реализовать методы equals и hashCode. В противном случае коэффициент Джека должен быть 35, как и ожидалось, потому что реализация по умолчанию hashCode возвращает то же значение, даже если вы изменили некоторые значения полей. Спасибо tw-abhi за указание!

Кстати, я также видел людей, использующих сеттеры с внедрением зависимостей в таких фреймворках, как Spring. Проверь это.

Никто не ожидает, что зависимость ParentService внезапно исчезнет. Но, как видите, это легко сделать и без Reflection API. Я хочу сказать, что если у вашего класса есть методы, которые могут изменять его состояние, это может привести к неприятным последствиям.

В любом случае, что мы можем с этим поделать? Что ж, самое простое - передать конструктору все необходимые значения.

Вы видите, что сейчас все объекты final? Это очень важно. Это означает, что значения могут быть присвоены только один раз в области действия конструктора. Неизменность гарантирует нам постоянное состояние объекта. Мы можем передать это любым методам. Мы даже можем безопасно получить к нему доступ из нескольких потоков.

А что, если нужны значения по умолчанию?. Что ж, Java не поддерживает значения аргументов по умолчанию, но мы можем определить несколько конструкторов, пропустив некоторые параметры. Или мы можем использовать Строитель-шаблон. Мы можем реализовать это сами, но я предпочитаю использовать библиотеку Lombok.

Итак, теперь очевидно, какой параметр назначен. Этот подход может показаться похожим на использование сеттеров, но есть огромная разница. Мы не создаем экземпляр объекта до вызова build метода. Но как только он создан, он неизменен.

Иногда разработчики используют строители для замены больших конструкторов. Это может показаться полезным, поскольку делает код более чистым и интуитивно понятным, но вы должны обращать внимание на такие случаи. Давайте посмотрим.

Дело в том, что второй подход значительно проще для понимания, но также легко забыть о некоторых аргументах. Это часто случается, особенно если класс был расширен новыми параметрами. Иногда это приемлемо, иногда нет. Не забывайте, что первоначальная цель шаблона Builder - реализация значений аргументов по умолчанию, а не замена больших конструкторов. Если у вашего класса много зависимостей и все они обязательны, было бы лучше разделить класс на несколько.

Заключение

Надеюсь, я убедил вас, что использование сеттеров - не лучшая практика. Бывают ситуации, когда приходится использовать сеттеры. Например, если одна библиотека предоставляет нам такой API. Но не используйте их в своих проектах. Это того не стоит. Спасибо за чтение!

Ресурсы