Czy powinniśmy zmienić nazwę przeciążonych metod?


14

Załóżmy interfejs zawierający te metody:

Car find(long id);

List<Car> find(String model);

Czy lepiej zmienić ich nazwy w ten sposób?

Car findById(long id);

List findByModel(String model);

Rzeczywiście, każdy programista, który korzysta z tego interfejsu API, nie musi patrzeć na interfejs, aby poznać możliwe argumenty find()metod początkowych .

Więc moje pytanie jest bardziej ogólne: Jaka jest korzyść z używania przeciążonych metod w kodzie, ponieważ zmniejszają one czytelność?


4
Każda metoda jest akceptowalna, o ile jesteś konsekwentny.
ChrisF

Istnieje związek między przeciążeniem a przesłanianiem metody. Jednak ten artykuł faworyzuje twoją sugestię - może być interesująca: roseindia.net/javatutorials/...
NoChance

Odpowiedzi:


23

Jest to stosunkowo niewielki problem w porównaniu z wieloma innymi praktykami złej czytelności, na które możesz być podatny, więc powiedziałbym, że to głównie kwestia gustu, jak nazywasz swoje metody.

Powiedziawszy to, jeśli zamierzasz coś z tym zrobić, postąpiłbym zgodnie z następującą praktyką:

  • Przeciążenie, jeśli ...

    Metody są zgodne z prawie tą samą umową, ale działają po prostu na różnych danych wejściowych (wyobraź sobie operatora telefonicznego, który może sprawdzić twoje konto na podstawie twojego osobistego numeru identyfikacji podatkowej, numeru konta lub imienia i daty urodzin). Obejmuje to zwracanie tego samego typu danych wyjściowych .

  • Użyj innej nazwy, jeśli ...

    Metody robią zasadniczo różne rzeczy lub zwracają różne dane wyjściowe (jak Twoja sprawa). Możesz rozważyć użycie innej nazwy, jeśli ktoś uzyskuje dostęp do bazy danych, a drugi nie.

    Ponadto, jeśli zwracany typ jest inny, zmieniłbym również czasownik, aby wskazać, że:

    Car findById(long id);
    
    List findAllByModel(String model);
    

3
FWIW, powiedziałbym to nieco mocniej: „Metody przestrzegają dokładnie tego samego kontraktu ...” Tzn. Typ / liczba argumentów nie ma znaczenia - semantyka wywołania funkcji jest identyczna niezależnie. Jeśli typ / liczba argumentów ma znaczenie, nie powinieneś przeciążać.
mcmcc

4

W każdym przypadku zaleciłbym użycie innej nazwy. Możliwe, że w przyszłości będziesz chciał dodać inną metodę, powiedzmy List<Car> findByMake(String make), w przeciwieństwie do List<Car> findByModel(String model). Nagle nazywanie wszystkiego findprzestaje mieć sens. Twoje metody są również mniej prawdopodobne, że zostaną przypadkowo użyte nieprawidłowo, jeśli ich nazwy podają więcej informacji o tym, jak należy je stosować.


1
Szczerze mówiąc, nie byłoby to aż tak dużym problemem, gdyby funkcjonalność była bardziej wyraźnie reprezentowana przez obiekty: find(Make val)i find(Model val). Wówczas metody wygody, takie jak, findByMake(String val)byłyby o wiele bardziej jasne, co faktycznie robią. W końcu a Stringnie jest ani marką, ani modelem, więc metoda powinna wyjaśnić, co tak naprawdę robi.
Nicole

4

Jeśli zmienisz nazwę metody, nie będzie ona już przeciążona. Samo przeciążenie niekoniecznie powoduje, że kod jest mniej czytelny, jednak może utrudnić wykonanie implementacji, jeśli składnia nie jest jasna.

Wiele języków używa przeciążania metod jako sposobu prezentacji interfejsu dla funkcjonalności, w której parametry mogą być opcjonalne i sugerowane są domyślne parametry opcjonalne. Jest to szczególnie prawdziwe w przypadku języków, które nie obsługują domyślnej składni parametrów w deklaracji metody.

Robiąc to:

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

ratuje cię przed zrobieniem tego:

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

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

Co jest bardziej czytelne, to naprawdę sprowadza się do ciebie. Osobiście wolę drugą opcję, szczególnie gdy lista parametrów staje się trochę długa, ale przypuszczam, że to nie ma znaczenia, o ile jesteś konsekwentny w całym interfejsie API.

Trudność z przeciążeniem pojawia się, gdy chcesz, aby funkcje, które wykonują zasadniczo to samo, i gdzie chcesz, aby listy parametrów były takie same, ale typy zwracane były różne. Większość języków nie wie, jak rozróżnić dwie metody o tej samej nazwie, ale z różnymi typami zwrotów. W tym momencie musisz pomyśleć o użyciu ogólnych, zmianie interfejsu parametrów lub zmianie nazwy jednej z metod, aby wskazać różnicę w typie zwrotu. Tutaj czytelność może stać się dużym problemem, jeśli nie zdecydujesz się na prosty i przejrzysty schemat nazewnictwa, aby poradzić sobie z takimi sytuacjami.

Nazywanie przeciążonych metod GetSomething()i GetSomethingEx()nie będzie dużo mówić o różnicach między metodami, szczególnie jeśli to typy zwracane są jedynymi różnicami między nimi. Z drugiej strony, GetSomethingAsInt()i GetSomethingAsString()powiem ci trochę więcej o tym, co robią metody, i chociaż nie jest to przeciążenie, wskazuj, że obie metody robią podobne rzeczy, ale zwracają różne typy wartości. Wiem, że istnieją inne sposoby nazywania metod, jednak w celu zilustrowania tego, te prymitywne przykłady powinny zrobić.

W przykładzie OP zmiana nazwy nie jest absolutnie niezbędna, ponieważ parametry metody są różne, ale to sprawia, że ​​określenie nazwy metody jest nieco bardziej precyzyjne. Ostatecznie sprowadza się to do rodzaju interfejsu, który chcesz przedstawić użytkownikom. Decyzja, czy nie przeciążać, nie powinna być podejmowana wyłącznie na podstawie własnego postrzegania czytelności. Przeciążenie metod może na przykład uprościć interfejs API i zmniejszyć liczbę metod, które deweloper może potrzebować pamiętać, z drugiej strony może zaciemnić interfejs w stopniu, który następnie wymaga od programisty przeczytania dokumentacji metody, aby zrozumieć, która forma metody do użycia, podczas gdy posiadanie wielu podobnie, ale opisowo nazwanych metod może sprawić, że bardziej oczywiste będzie po prostu czytanie nazwy metody co do jej celu.


0

Preferuj przeciążanie, dopóki metody zwracają to samo i postępują zgodnie z tą samą umową. Przeładowanie uwalnia kod wywołujący od niepotrzebnego zatwierdzania typu parametru.

Załóżmy, że funkcja wywołująca odbiera zapytanie jako parametr i wykonuje inne przetwarzanie przed i / lub po wywołaniu find.

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

Jeśli chcesz zmienić typ tego zapytania z dowolnego powodu (np. Z prostego ciągu identyfikatora na w pełni funkcjonalny obiekt zapytania jakiegoś rodzaju), możesz wprowadzić tę zmianę w funkcji wywołującej po prostu zmieniając podpis funkcji aby zaakceptować nowy typ parametru bez obawy o zmianę metody wywoływanej w klasie.

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

Jeśli wdrożysz findByIdi findByQueryObjectosobno, będziesz musiał wyśledzić każde połączenie, aby wprowadzić tę zmianę. W przykładzie zmieniłem tylko jedno słowo i skończyłem.


Ta odpowiedź zakłada oczywiście, że używasz języka, który obsługuje przeciążanie z błędami czasu kompilacji dla niepoprawnego parametru. Jeśli piszesz JavaScript, Ruby lub jakikolwiek inny język, który natywnie nie obsługuje przeciążania, zawsze używałbym verbose, findByFooaby wcześniej wykryć niezgodności typów.
sqykly
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.