Должны ли мы переименовать перегруженные методы?

14

Предположим интерфейс, содержащий эти методы:

Car find(long id);

List<Car> find(String model);

Лучше переименовать их так?

Car findById(long id);

List findByModel(String model);

Действительно, любому разработчику, использующему этот API, не нужно смотреть на интерфейс, чтобы узнать возможные аргументы начальных find()методов.

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

Mik378
источник
4
Любой метод приемлем, если вы последовательны.
ChrisF
Существует взаимосвязь между перегрузкой и переопределением метода. Однако эта статья способствует ваше предложение - это может заинтересовать: roseindia.net/javatutorials/...
NoChance

Ответы:

23

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

С учетом сказанного, если вы собираетесь что-то с этим делать, я бы следовал этой практике:

  • Перегрузка, если ...

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

  • Используйте другое имя, если ...

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

    Кроме того, если возвращаемый тип отличается, я бы также изменил глагол, чтобы указать, что:

    Car findById(long id);
    
    List findAllByModel(String model);
    
Николь
источник
3
FWIW, я бы слово , которое немного сильнее: «Методы подчиняются точно тот же контракт ...» Т.е. аргумент типа / счетчик не имеет значения - семантика вызова функции идентичны независимо. Если тип аргумента / число имеет значение, то вам не следует перегружать.
mcmcc
4

Я бы рекомендовал использовать другое имя в каждом случае. Возможно, что в будущем вы захотите добавить другой метод, скажем List<Car> findByMake(String make), в отличие от List<Car> findByModel(String model). Так внезапно, звонить все findперестает иметь смысл. Ваши методы также с меньшей вероятностью будут непреднамеренно использованы неправильно, если их имена дают больше информации о том, как их следует использовать.

Дауд говорит восстановить Монику
источник
1
Честно говоря, это не было бы проблемой, если бы функциональность была более явно представлена ​​объектами: find(Make val)и find(Model val). Тогда удобные методы, такие как findByMake(String val), будут намного более ясными, что они фактически делают. В конце концов, a Stringне является ни маркой, ни моделью, поэтому метод должен объяснить, что он на самом деле делает.
Николь
4

Если вы переименуете метод, он больше не будет перегружен. Сама по себе перегрузка не обязательно делает код менее читабельным, однако она может усложнить реализацию, если синтаксис не ясен.

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

Так делаем это:

void MyMethod(int param1, int param2 = 10)
{
    ...
}

избавляет вас от этого:

void MyMethod(int param1)
{
    MyMethod(param1, Param2Default);
}

void MyMethod(int param1, int param2)
{
    ....
}

Что касается того, что является более читабельным, то это действительно сводится к вам. Лично я предпочитаю второй вариант, особенно когда список параметров становится немного длиннее, но я полагаю, что это не имеет большого значения, если вы последовательны в своем API.

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

Присвоение имен перегруженных методов GetSomething()и GetSomethingEx()не будет много говорить о том, что различия между вашими методами, особенно если она является возвращаемые типы, которые только различия между ними. С другой стороны, GetSomethingAsInt()иGetSomethingAsString() расскажем вам немного больше о том, что делают методы, и хотя они не являются строго перегрузочными, они указывают на то, что оба метода выполняют схожие функции, но возвращают разные типы значений. Я знаю, что есть другие способы, которыми вы могли бы называть методы, однако для иллюстрации, эти грубые примеры должны подойти.

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

S.Robins
источник
0

Избегайте перегрузки, если методы возвращают одно и то же и следуют одному и тому же контракту. Перегрузка освобождает вызывающий код от ненужной фиксации к типу параметра.

Предположим, что вызывающая функция получает поисковый запрос в качестве параметра и выполняет некоторую другую обработку до и / или после вызова find.

void tryToSellCars(String which) {
    /* grab an airhorn, inflatable tube guy... */
    List<Car> cars = find(which);
    /* expound virtues of each car in detail... */
}

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

void tryToSellCar(CarQuery which) {
    /* grab airhorn, inflate tube guy... */
    List<Car> cars = find(which)
    /* expound virtues of each car in detail... */
}

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

sqykly
источник
Этот ответ предполагает, конечно, что вы используете язык, который поддерживает перегрузку с ошибками времени компиляции для недопустимого параметра. Если вы пишете JavaScript, Ruby или любой другой язык, который изначально не поддерживает перегрузку, я бы всегда использовал подробный метод findByFooдля выявления несоответствий типов ранее.
sqykly