Чи слід перейменувати перевантажені методи?


14

Припустимо інтерфейс, що містить ці методи:

Car find(long id);

List<Car> find(String model);

Чи краще перейменувати їх так?

Car findById(long id);

List findByModel(String model);

Дійсно, будь-який розробник, який використовує цей API, не повинен шукати інтерфейс, щоб знати можливі аргументи початкових find()методів.

Отже, моє питання є більш загальним: Яка користь від використання перевантажених методів у коді, оскільки це зменшує читабельність?


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()розкажіть вам трохи більше про те, що роблять методи, і, хоча не є суворо перевантаженим, вказуйте на те, що два методи роблять подібні речі, але повертають різні типи значень. Я знаю, що існують і інші способи, якими ви могли б назвати методи, однак для того, щоб проілюструвати суть, ці грубі приклади повинні робити.

У прикладі ОП перейменування не є суворо необхідним, оскільки параметри методу різні, однак це робить дещо зрозумілішим назву методу конкретніше. Зрештою, це дійсно зводиться до типу інтерфейсу, який ви хочете представити своїм користувачам. Рішення про те, чи не перевантажувати, не повинно прийматися виключно на основі власного сприйняття читабельності. Методи перевантаження можуть, наприклад, спростити інтерфейс API і зменшити кількість методів, про які розробник може запам'ятати, з іншого боку, він може заплутати інтерфейс до такої міри, яка потім вимагає від розробника ознайомитися з документацією методу, щоб зрозуміти, у якій формі використовуваного методу, тоді як наявність ряду подібних, але описово названих методів може зробити більш очевидним лише читання назви методу щодо його мети.


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окремо, вам доведеться вишукувати кожен виклик, щоб змінити цю ситуацію. У прикладі я змінив лише одне слово, і я закінчив.


Ця відповідь, звичайно, передбачає, що ви використовуєте мову, яка підтримує перевантаження помилками компіляції для невірного параметра. Якщо ви пишете JavaScript або Ruby або будь-яку іншу мову, яка не підтримує перевантаження, я завжди використовую багатослівний текст, findByFooщоб раніше виявити невідповідність типу.
sqykly
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.