Constructors Must Be Code-Free

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

На сколько работы следует выполнять в конструкторе? Кажется разумным проводить вычисления внутри конструктора и после этого инкапсулировать результаты. Таким образом, когда результаты потребуются методам объекта, у нас они уже будут готовы. Звучит как хороший подход? Нет, это плохая идея по одной причине: она препятствует композиции объектов и делает их не расширяемыми.

Предположим, мы создаем интерфейс, который представляет имя человека:

Довольно просто, верно? Теперь давайте попробуем воплотить это в жизнь.

Что не так с этим? Это быстрее, верно? Он разбивает имя на части только один раз и инкапсулирует их. Затем, несмотря на то, сколько раз мы вызываем метод first(), он будет возвращать одно и то же значение и не будет нуждаться в повторном разбиении. Однако, это неправильное мышление! Позвольте мне показать вам правильный путь и объяснить:

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

Прежде чем начать доказательство, позвольте мне попросить вас прочитать эту статью: Composable Decorators vs. Imperative Utility Methods. В ней объясняется разница между статическим методом и композиционными декораторами. Первый фрагмент выше очень близок к императивному утилитарному методу, хотя и выглядит как объект. Второй пример является истинным объектом.

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

Давайте попробуем использовать наш класс EnglishName:

В первой строке этого фрагмента мы просто создаем экземпляр объекта и помечаем его как name. Мы пока не хотим обращаться к базе данных и извлекать полное имя оттуда, разделять его на части и инкапсулировать их внутри name. Мы просто хотим создать экземпляр объекта. Такое поведение разбора будет для нас побочным эффектом и в данном случае замедлит работу приложения. Как видите, нам может понадобиться только name.first(), если что-то пойдет не так и нам понадобится создать объект исключения.

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

Что касается производительности при повторном использовании name, вы можете спросить. Если мы создаем экземпляр EnglishName и затем вызываем name.first() пять раз, мы получим пять вызовов метода String.split().

Чтобы решить эту проблему, мы создаем еще один класс - составной декоратор, который поможет нам решить эту проблему “повторного использования”:

Я использую аннотацию Cacheable из jcabi-aspects, но вы можете использовать любые другие инструменты кэширования, доступные в Java (или других языках), например Guava Cache:

Но, пожалуйста, не делайте CachedName изменяемым и отложенной загрузки. Это антипаттерн, о котором я уже говорил в статье “Объекты должны быть неизменяемыми”.

Вот как будет выглядеть наш код теперь:

Это очень примитивный пример, но я надеюсь, что вы понимаете идею.

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

Позвольте мне повторить, что единственное разрешенное выражение внутри конструктора - это присваивание. Если вам нужно поместить что-то еще туда, начните думать о рефакторинге - вашему классу определенно нужен пересмотр.

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-17 at 14:10

sixnines availability badge   GitHub stars