Коли перевантаження методу підходить?


10

Припустимо, я працюю над існуючою, досить великою системою. У мене є myObjectклас класу MyClass(для прикладу, припустимо, я працюю в Java). myObjectце композиція, що містить Collection, скажімо, а, Listта інші об'єкти, які (я думаю) не мають значення. Він містить делегатні методи, які просто служать для використання методів, з яких Listвін складається, для того, щоб переконатися, що Listвін не піддається (вибачте, якщо я неправильно визначив свою термінологію).

Скажімо, що це List, List<String>але, чомусь, основний метод доступу - це метод маски для класу SomeOtherClass. Якби я хотів вставити нову пару значень у свою List, я мав би об'єкт, який SomeOtherClassназивається someObject. Я б закликав, myObject.insert(someObject)і всередині insertметоду знайдеться якась магія, яка дозволить отримати a, Stringщоб поставити його List<String>.

Припустимо тепер, що у мене є лише Stringзначення, і жоден SomeOtherClassоб’єкт для вставки. Якщо припустити, що я не можу змінити insertметод, оскільки він порушив би все в цій системі. Тоді я повинен перевантажувати insertметод? Або я повинен створювати новий об'єкт SomeOtherClassкожного разу, коли я хочу зателефонувати insert?

Я думаю, якби я це перевантажив, це виглядало б приблизно так ...

public void insert(String s) {
    ...
}

public void insert(SomeOtherObject obj) {
    this.insert(obj.magicStringMethod());
}

(Цей приклад - це надумана головоломка, заснована на подібній (дещо складнішій) ситуації щодо перевантаження, з якою я зіткнувся вчора. Я розгорну її, якщо буде щось незрозуміле)

Це було б відповідним місцем для перевантаження методу? Якщо ні, коли я повинен перевантажувати метод?


Якщо припустити Java, ви вирішили використати Generics для вирішення цієї проблеми? Я здогадуюсь, що я насправді запитую, що насправді являє струна? Якщо це насправді репрезентація фактичного об’єкта домену, то є ще один потенційний спосіб вирішити цю проблему
Martijn Verburg

@MartijnVerburg На цьому етапі я ще ні. Оскільки я лише стажист, я все ще трохи не знайомий з такими речами, як дизайнерські зразки, і ще не маю гарних звичок дизайнерської практики. Відповідаючи на ваше друге запитання, це представлення фактичного об’єкта домену. magicStringMethod()у моєму прикладі, у моєму випадку, вийшло б Stringпредставлення того, SomeOtherObjectякий був доменним об’єктом.
бламан

Я пропоную вам залишитися подалі від перевантаження, коли зможете. Іноді це викликає плутанину. Найкраще бути певним щодо названого методу під час проектування. Дивіться це: programmers.stackexchange.com/questions/132369/…
NoChance

Відповіді:


7

Ви перевантажуєтесь, коли хочете підтримувати різні типи:

public overload void MyMethod(int value)
{ 
}

public overload void MyMethod(bool value)
{
}

public overload void MyMethod(string value)
{
}

або підтримувати прогресивний інтерфейс, використовуючи різні списки параметрів:

public overload void MyOtherMethod()
{
    this.MyOtherMethod(DefaultValue);
}

public overload void MyOtherMethod(int value)
{
    this.MyOtherMethod(value, DefaultOtherValue);
}

public overload void MyOtherMethod(int value, bool otherValue)
{
    ...
}

Ви можете навіть трохи з розуму і підтримувати обидва типи І прогресивний інтерфейс, але ви повинні мати на увазі, що вам потрібно уникати змін у поведінці між перевантаженими методами. Кожен перевантажений метод повинен бути функціонально таким же, як і інші у перевантаженій групі, інакше буде незрозуміло, як, коли або чому змінюється поведінка. Якщо ви хочете, щоб дві функції робили щось зовсім інше, то вам слід назвати їх відповідно.


7

Я б сказав, що перевантаження доречна, коли обидва способи семантично рівнозначні. Викрадати приклади герцогства:

Вони перевантажені належним чином:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a+b;
}

Це не такі:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a-b;
}

Це крайній приклад (якщо ви його не вловили, останній метод насправді віднімає замість додавання), але ідея полягає в тому, що якщо у вас є кілька методів у класі з тим самим іменем, вони повинні вести себе послідовно.

У вашому прикладі, з наведеної інформації вони здаються еквівалентними (оскільки один дзвонить іншим), і я вважаю за доцільне їх перевантажувати. Не завадить запитати когось старшого у вашій команді, якщо ви не впевнені в тому, чи слід додавати метод, але якщо ви його додали, я використовував би те саме ім’я (тобто я би перевантажував його).


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

5

По суті, щоб параметри методу диктували, як метод буде вести себе.

Швидкий приклад:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    //...
}

Якщо ви передасте методу sum (a, b) пару цілих чисел, він знає, що він повинен викликати першу реалізацію, оскільки виклик методу збігається з підписом методу (тобто подвійна сума (double, double) не буде працювати, якщо ви дасте метод суми два цілі числа).

Підпис дзвінка повинен відповідати доступним реалізаціям, тому спроба виклику суми (рядка, рядка) не буде працювати, якщо у вас цього немає:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    public string sum(String a, String b){
        return "" + (Double.parseDouble(a) + Double.parseDouble(b));
    }
    //...
}

tl; dr: щоб клас обробляв правильний метод відповідно до будь-яких параметрів, яким ви даєте метод з однаковою назвою.

При успадкуванні це називається переосмисленням

Хорошим прикладом цього іншого сценарію є те, коли ви хочете змінити поведінку класу за замовчуванням, успадкувавши його, і особливо, коли у вас є щось подібне до шаблону методу шаблону, де кожен крок деякого алгоритму реалізований у методі.

Уявіть, що у вас є class Robotі метод під назвою fireAtTarget(Target target)... який дзвонить fireWeaponA(target); fireWeaponB(target); fireWeaponC(target);один за одним. Ви також хочете мати колекцію під назвою robot_army, до якої ви можете додавати лише об'єкти класу Robot. У своїх fireWeaponX()методах робот за замовчуванням стріляє з кулеметів .

Тоді ви хочете мати, class LazorRobotі class MissileRobotчи реалізуєте ви Робота знову? Ні, просто LazorRobot і MissileRobot успадковують Робота, і перевантажуйте кожен fireWeaponX()метод, щоб використовувати лазор або ракети, і ви матимете таку саму поведінку з різною зброєю, не потребуючи повторного доповнення решта методів.

tl; dr: поведінка методу залежить від класу, не порушуючи інтерфейс (robot_army приймає тільки Robot, але приймає шляхом розширення будь-які класи, які успадковують робота).


Ваш перший приклад - переоцінка . Коли ви підтримуєте інтерфейс методу, проте змінюйте поведінку у нащадка. Мається на увазі, що ви не маєте наміру пропонувати альтернативний метод. Однак ваш другий приклад - це перевантаження. Якщо ваш намір полягає в тому, щоб підкреслити, коли переосмислити vs, коли перевантажувати, ви, можливо, захочете зробити це зрозумілішим у своїй відповіді, інакше весь when inheritingрозділ, мабуть, не потрібен. :)
Робінз

Дерп, кофеїн отримав мене цього разу = Р, залишив другу частину як першу, а першу як другу для загального інтересу. Дякуємо за виправлення.
герцогство.

Якщо я правильно зрозумів вашу відповідь, я повинен перевантажуватися лише тоді, коли дивлячись на параметри, дозволять мені зробити висновки про відмінності в поведінці між, скажімо, sum(String, String)і sum(int, int)?
бламан

@blahman вам слід перевантажуватися, коли ви хочете використовувати різні типи, суміш типів або навіть для додавання додаткових параметрів. Перевантаження дає спосіб створити методи з параметрами за замовчуванням, де синтаксис параметр = значення за замовчуванням не підтримується. Дивіться мою відповідь, щоб побачити, що я маю на увазі.
S.Robins

1
@blahman BTW та інший предмет, коли у вас занадто багато параметрів, а деякі з них необов'язкові, іноді краще просто надіслати один об'єкт або структуру даних, які мають усі значення за замовчуванням, тому ви зміните атрибути такого об’єкта чи даних будова. Таким чином, вам не потрібно робити 20 версій одного методу, щоб кожного разу додавати один параметр.
герцогство, яке відбулося
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.