Значения-классы
В очередной раз читаю спор а-ля “динамическая типизация против статической”. Заметил вот что.
Попадаются отчаянные джависты, которые говорят: даже простые значения нужно
делать классами, чтобы не допустить ошибки. Например, телефон должен быть не
строкой, а классом Phone, который оборачивает строку; почта — классом Email,
имя — Name и так далее.
Идея в том, что если метод принимает имя, телефон и почту, то строки легко
перепутать: передать почту вместо имени или телефон вместо почты. А если
параметры типизированы — Name, Email, Phone, — то ошибки быть не может.
То же самое предлагается делать с числами: температура по Фаренгейту — один
класс, по Цельсию — другой. Для коров и лошадей тоже разные классы:
HorseNumber и CowNumber. У таких классов свои методы сложения и вычитания,
чтобы можно было сложить коров только с коровами, но не дай бог с лошадьми.
К счастью, в Джаве нельзя наследоваться от базовых классов вроде String,
Number и других. Каким-то образом ее создатели догадались, что если каждый
Васян будет делать свои строки и числа, то откроется дверь в ад.
Но нашлись лазейки. Иные джависты делают записи с одним полем value, примерно
так:
public record Phone(value String) {};
Phone phone = new Phone("223-322");
Примеры, что они приводят, на первый взгляд красивы. Сигнатура как в примере ниже
someMethod(Name name, Email email, Phone phone)
действительно выглядит лучше, чем String, String, String.
Однако первая беда в том, что ничто не мешает мне выполнить такой код:
Phone phone = new Phone("test@hello.com");
и передать этот псевдо-телефон в метод. Можно добавить валидацию в конструктор,
но в любом случае она сработает в рантайме — компилятор ничего не знает о том,
что строка test@hello.com — это неправильный телефон.
А вторая беда в том, что классы не дружат между собой. Даже если ты наколбасил
классы телефона, почты и числа коров, они не подойдут другим таким же
классам. Например, коллега создал класс UserPhone, полностью аналогичный
вашему. Компилятору все равно, что они одинаковы: нельзя передать Phone туда,
где ожидается UserPhone и наоборот. Все это закончится конвертацией в духе:
UserPhone ph = new UserPhone(phone.value());
Джавистам, которые мечтают о таком подходе, я бы пожелал столкнуться с ним на
практике. Чтобы у них был проект с классами ЧислоЯблок, ЯблокВЯщике,
ЧислоЯщиков, ВозрастПользователя, ВозрастСотрудника, КуритИлиНет,
МужчинаИлиЖенщина и так далее. Как говорится, бойся своих желаний!
Кстати, чтобы избежать путаницы в параметрах, они должны поддерживать передачу по имени, например так:
new User(name="...", phone="...", email="...");
В Джаве такого нет; там приняты билдеры:
User.builder().name("...").phone("...").email("...").build();
Билдеры занимают экраны кода, но с таким синтаксисом легче понять семантику параметров.
Общая проблема в том, что джависты не видят баланса между безопасностью и удобством. Если ради этой вашей безопасности — которая все еще под вопросом — нужно поступиться удобством, то я не согласен.
Нашли ошибку? Выделите мышкой и нажмите Ctrl/⌘+Enter