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()
определен дважды с двумя разными сигнатурами и реализациями):
class Cart {
private final List<Product> products;
void add(int id) {
this.add(new Catalog().findById(id));
}
void add(Product p) {
this.products.add(p);
}
}
var c = new Cart();
c.add(new Product("book"));
c.add(42);
Этот подход действительно удобен по нескольким причинам. Во-первых, метод add(int)
обрабатывает преобразование из int
в Product
, которое не требуется повторять в других местах — они могут просто передать идентификатор продукта в этот метод и позволить ему сделать всю работу, тем самым устраняя дублирование кода. Во-вторых, поскольку функциональность “поиска в каталоге” не выходит за пределы класса Cart
, это упрощает окружающий код. Кажется, что действительно улучшается удобство использования и читаемость кода.
Однако, проблемы, которые решаются (дублирование кода и сложность), меньше, чем проблемы, которые вносятся. В то время как семантика метода add(Product)
очевидна, работа метода add(int)
неясна для его пользователей. Возможно, он ищет в каталоге? Может быть, он выбирает n-й продукт из существующей корзины и добавляет его в конец корзины? Или, может быть, он ищет заказы, ранее размещенные пользователем, и извлекает n-й продукт оттуда? Мы просто не знаем, когда изучаем сигнатуру метода.
Для того чтобы понять, что делает метод add(int)
, мы должны обратиться к его блоку Javadoc, который может быть недостаточно точным. Более того, как часто бывает, документация в блоке Javadoc может не соответствовать коду внутри метода. Проще говоря, клиент, использующий метод add(int)
, неизбежно делает предположение о его внутренней работе. Если клиенту повезёт, 1) предположение будет верным, и 2) любые последующие изменения метода не нарушат это предположение.
Кажется, что перегрузка методов является источником проблемы: поддержка нескольких реализаций под одним именем неизбежно вводит скрытую семантику в большинстве случаев. Затем, когда что-то затуманивается, это вызывает предположения, которые в свою очередь приводят к недоразумениям и в конечном итоге к ошибкам.
Я считаю, что лучшей альтернативой будет выглядеть следующим образом:
class ProductInCatalog implements Product {
private final Product p;
ProductInCatalog(int id) {
this.p = new Catalog().findById(id);
}
// all "Product" interface's methods
}
class Cart {
private final List<Product> products;
void add(Product p) {
this.products.add(p);
}
}
var c = new Cart();
c.add(new Product("book"));
c.add(new ProductInCatalog(42));
Конструктор ProductInCatalog
не является без кода, как было бы идеально, однако, это не так важно для нашего текущего обсуждения. Класс ProductInCatalog
служит абстракцией Product
, найденного в каталоге. Этот класс используется клиентом класса Cart
. Клиент, полностью осознавая и с явным намерением, преобразует 42 (ID продукта) в экземпляр ProductInCatalog
. Этот дизайн больше не скрывает никаких элементов. Нет необходимости делать предположения и нет условий, которые код должен выполнять.
Мы все еще сохраняем преимущества, предоставленные перегрузкой методов? Действительно, да. Нет дублирования кода и сложность кода снижается. Кроме того, улучшается читаемость кода с использованием Cart
. Подводя итог, я рекомендую избегать перегрузки методов, хотя это, безусловно, приведет к большему количеству классов в кодовой базе. Однако это уже совсем другая дискуссия.
Translated by ChatGPT gpt-3.5-turbo/35 on 2023-09-08 at 16:30