The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
Перегрузка методов - это распространенная возможность во многих языках программирования, позволяющая классу иметь два или более метода с одинаковым именем, но разными параметрами. Согласно компании Microsoft, перегрузка методов - “одна из самых важных техник для улучшения удобства использования, производительности и читаемости повторно используемых библиотек.” Я не согласен. По моему мнению, перегрузка методов может привести к менее читаемому коду и большему количеству ошибок, потому что поддержание двух или более реализаций под одним именем приводит к скрытым семантикам, которые неизбежно приводят к недоразумениям и функциональным дефектам.
Давайте начнем с примера на языке Java. Предположим, вы хотите добавить товар в корзину, имея при этом либо идентификатор товара, либо объект Product
. Если предоставлен только идентификатор, вы хотели бы, чтобы корзина обратилась к каталогу товаров, нашла соответствующий объект Product
и добавила его. Вот где перегрузка методов может быть полезной (метод add()
определен дважды с двумя разными сигнатурами и реализациями):
Этот подход действительно удобен по крайней мере по двум причинам. Во-первых, метод add(int)
обрабатывает преобразование из int
в Product
, которое не нужно повторять в других местах - вместо этого можно просто передать ID товара в этот метод и позволить ему выполнить работу, тем самым устраняя дублирование кода. Во-вторых, поскольку функциональность “поиска в каталоге” не раскрывается за пределами класса Cart
, это упрощает окружающий код. Кажется, что действительно улучшаются удобство использования и читаемость кода.
Однако решаемые проблемы (дублирование кода и сложность) меньше, чем вводимые проблемы. Если семантика метода add(Product)
очевидна, то работа метода add(int)
не ясна его пользователям. Возможно, он ищет товар в каталоге? Может быть, он выбирает n-й товар из существующей корзины и добавляет его в конец корзины? Или, возможно, он ищет заказы, ранее размещенные пользователем, и извлекает из них n-й товар? Мы просто не знаем, когда изучаем сигнатуру метода.
Чтобы понять, что делает метод add(int)
, нам нужно обратиться к его Javadoc-блоку, который может быть недостаточно точным. Более того, как это часто бывает, документация в Javadoc-блоке может не соответствовать коду внутри метода. Проще говоря, клиент, использующий метод add(int)
, неизбежно делает предположение о его внутреннем устройстве. Если клиенту повезло, 1) предположение будет верным, и 2) любые последующие изменения метода не недопустимы это предположение.
Кажется, что перегрузка методов является корнем проблемы: поддержка нескольких реализаций под одним именем неизбежно вводит некоторые скрытые семантические моменты в большинстве из них. Затем, когда что-то затуманено, возникают предположения, которые, в свою очередь, приводят к недоразумениям и, в конечном итоге, к ошибкам.
Я считаю, что лучшей альтернативой будет следующее:
Конструктор ProductInCatalog
не является безопасным с точки зрения кода, каким он должен быть, однако это не так важно для нашего текущего обсуждения. Класс ProductInCatalog
служит абстракцией Product
, найденной в каталоге. Этот класс используется клиентом класса Cart
. Клиент, полностью осознавая и с явным намерением, преобразует 42 (идентификатор продукта) в экземпляр ProductInCatalog
. Этот дизайн больше не скрывает никаких элементов. Необходимо делать предположения и нет условий, которые код должен выполнять.
Сохраняются ли преимущества, предоставляемые перегрузкой методов? Действительно, да. Нет дублирования кода, и сложность кода уменьшается. Кроме того, читаемость кода при использовании Cart
значительно улучшается. В итоге, я рекомендую избегать перегрузки методов, даже если это, несомненно, приведет к большему количеству классов в кодовой базе. Однако это уже совсем другое обсуждение.
Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-28 at 15:47