Kiedy właściwe jest przeładowanie metody?

10

Załóżmy, że pracuję na istniejącym, dość dużym systemie. Mam obiekt myObjectklasy MyClass(dla przykładu załóżmy, że pracuję w Javie). myObjectto kompozycja zawierająca Collection, powiedzmy, a Listi inne przedmioty, które (myślę) są nieistotne. Zawiera metody delegowania, które służą tylko do wywołania metod, z których Listsię składa, w celu zapewnienia, Listże nie zostały ujawnione (przepraszam, jeśli moja błędna terminologia jest nieprawidłowa).

Powiedzmy, że Listjest to, List<String>ale z jakiegoś powodu główną metodą dostępu jest metoda maskowania klasy SomeOtherClass. Gdybym chciał wstawić nową parę wartości do siebie List, miałbym obiekt o SomeOtherClassnazwie someObject. Wywołałbym myObject.insert(someObject)i w insertmetodzie byłaby jakaś magia, która odzyskałaby a Stringdo włożenia List<String>.

Załóżmy teraz, że mam tylko Stringwartość i nie mam SomeOtherClassobiektu do wstawienia. Zakładając, że nie mogę zmodyfikować insertmetody, ponieważ zepsułaby wszystko w tym systemie. Czy powinienem przeciążyć insertmetodę? Czy powinienem utworzyć nowy obiekt za SomeOtherClasskażdym razem, gdy chcę zadzwonić insert?

Wydaje mi się, że gdybym go przeciążył, wyglądałoby to tak ...

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

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

(Ten przykład jest wymyśloną łamigłówką opartą na podobnej (nieco bardziej złożonej) sytuacji dotyczącej przeciążenia, z którą się wczoraj spotkałem. Rozwinę ją, jeśli będzie coś niejasnego)

Czy byłoby to odpowiednie miejsce do przeciążenia metody? Jeśli nie, kiedy powinienem przeciążyć metodę?

blahman
źródło
Zakładając Javę, czy zastanawiałeś się nad użyciem Generics do rozwiązania tego problemu? Wydaje mi się, że tak naprawdę pytam o to, co tak naprawdę Łańcuch reprezentuje? Jeśli tak naprawdę jest to reprezentacja rzeczywistego obiektu domeny, istnieje inny potencjalny sposób rozwiązania tego problemu
Martijn Verburg
@MartijnVerburg Nie mam na tym etapie. Ponieważ jestem tylko stażystą, nadal nie jestem zaznajomiony z takimi wzorami projektowymi i nie mam jeszcze dobrych nawyków projektowych. W odpowiedzi na twoje drugie pytanie jest to reprezentacja rzeczywistego obiektu domeny. magicStringMethod()w moim przykładzie uzyskałbym w moim przypadku Stringreprezentację, SomeOtherObjectktóra była obiektem domeny.
blahman
Sugeruję, abyś unikał przeciążania, kiedy możesz. Czasami powoduje zamieszanie. Najlepiej mieć pewność co do wywoływanej metody w czasie projektowania. Zobacz: programmers.stackexchange.com/questions/132369/…
NoChance

Odpowiedzi:

7

Przeciążasz, gdy chcesz obsługiwać różne typy:

public overload void MyMethod(int value)
{ 
}

public overload void MyMethod(bool value)
{
}

public overload void MyMethod(string value)
{
}

lub do obsługi progresywnego interfejsu przy użyciu różnych list parametrów:

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)
{
    ...
}

Możesz nawet trochę zwariować i obsługiwać oba różne typy ORAZ interfejs progresywny, ale powinieneś pamiętać, że musisz unikać tworzenia różnic w zachowaniu między przeciążonymi metodami. Każda przeciążona metoda powinna być funkcjonalnie taka sama jak inne w przeciążonej grupie, w przeciwnym razie nie będzie jasne, jak, kiedy i dlaczego zachowanie się zmienia. Jeśli chcesz, aby dwie funkcje zrobiły coś zupełnie innego, powinieneś odpowiednio je nazwać.

S.Robins
źródło
7

Powiedziałbym, że przeciążenie jest właściwe, gdy obie metody są semantycznie równoważne. Aby ukraść z przykładów dukeofgaming:

Są one odpowiednio przeciążone:

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

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

Te nie są:

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

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

Jest to skrajny przykład (jeśli go nie złapałeś, ostatnia metoda faktycznie odejmuje zamiast dodawać), ale pomysł jest taki, że jeśli masz wiele metod w klasie o tej samej nazwie, powinny one zachowywać się konsekwentnie.

W twoim przykładzie z podanych informacji wydają się one równoważne (ponieważ jedno wywołuje drugie) i uważam, że rozsądne byłoby ich przeciążenie. Zapytanie kogoś wyższego szczebla w zespole nie może zaszkodzić, jeśli nie masz pewności, czy metoda powinna zostać dodana, ale jeśli ją dodasz, użyłbym tej samej nazwy (tzn. Przeładowałbym ją).

Gyan alias Gary Buyn
źródło
1
Dziękuję za odpowiedź. Poprosiłem kogoś starszego, ale ponieważ kod jest w dość interesującym stanie, ich rozwiązanie nie było pewne, ale mimo to zaimplementowane (to nie było przeciążenie).
blahman
5

Zasadniczo posiadanie parametrów metody decyduje o jej zachowaniu.

Szybki przykład to:

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

Jeśli przekażesz metodzie sumę (a, b) kilka liczb całkowitych, to wie, że powinna ona wywołać pierwszą implementację, ponieważ wywołanie metody pokrywa się z sygnaturą metody (tj. Podwójna suma (podwójna, podwójna) nie zadziała, jeśli podasz metoda sumy dwie liczby całkowite).

Podpis wywołania musi być zgodny z dostępnymi implementacjami, więc próba wywołania sumy (ciąg, ciąg) nie będzie działać, jeśli nie masz:

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: Aby klasa obsłużyła poprawną metodę zgodnie z parametrami podanymi dla metody o tej samej nazwie.

Podczas dziedziczenia nazywa się to przesłonięciem

Dobrym przykładem tego innego scenariusza jest sytuacja, w której chcesz zmienić domyślne zachowanie klasy, dziedzicząc ją, a zwłaszcza gdy masz coś podobnego do wzorca metody szablonu , gdzie każdy krok algorytmu jest zaimplementowany w metodzie.

Wyobraź sobie, że masz class Roboti metodę o nazwie fireAtTarget(Target target)... która wywołuje fireWeaponA(target); fireWeaponB(target); fireWeaponC(target);jeden po drugim. Chcesz także mieć kolekcję o nazwie robot_army, do której możesz tylko dodawać obiekty klasy Robot. Robot strzela domyślnie w swoje fireWeaponX()metody karabinami maszynowymi .

Więc chcesz mieć class LazorRoboti class MissileRobotczy wdrażasz Robot od nowa ?, nie, po prostu LazorRobot i MissileRobot odziedziczą Robota i przeciążaj każdą fireWeaponX()metodę korzystania z Lazorza lub pocisków, a będziesz mieć takie samo zachowanie z inną bronią, bez konieczności ponownej implementacji pozostałe metody.

tl; dr: Aby zachowanie metody zależało od klasy bez zerwania interfejsu (robot_army akceptuje tylko Robota, ale przez rozszerzenie akceptuje wszystkie klasy, które dziedziczą Robota).

dukeofgaming
źródło
Twój pierwszy przykład to zastąpienie . Gdy utrzymujesz interfejs metody, zmieniaj zachowanie potomka. Oznacza to, że nie zamierzasz oferować alternatywnej metody. Twój drugi przykład jednak poprawnie przeciąża. Jeśli Twoim celem jest podkreślenie, kiedy przesłonić, a kiedy przeładować, możesz to wyjaśnić w swojej odpowiedzi, w przeciwnym razie cała when inheritingsekcja prawdopodobnie nie będzie potrzebna. :)
S.Robins
Derp, kofeina dostała mnie tym razem = P, pozostawiła drugą część jako pierwszą i pierwszą jako drugą dla ogólnego zainteresowania. Dziękuję za poprawienie mnie.
dukeofgaming
Jeśli dobrze rozumiem twoją odpowiedź, powinienem przeciążać się tylko wtedy, gdy spojrzenie na parametry pozwoli mi wywnioskować różnice w zachowaniu między, powiedzmy, sum(String, String)a sum(int, int)?
blahman
@ blahman powinieneś przeciążać, gdy chcesz użyć różnych typów, kombinacji typów, a nawet dodać dodatkowe parametry. Przeciążenie umożliwia tworzenie metod z parametrami domyślnymi, w których domyślna składnia param = wartość nie jest obsługiwana. Zobacz moją odpowiedź, aby zobaczyć, co mam na myśli.
S.Robins
1
@blahman BTW i na inny temat, jeśli masz zbyt wiele parametrów, a niektóre z nich są opcjonalne, czasem lepiej jest po prostu wysłać pojedynczy obiekt lub strukturę danych, która ma wszystkie wartości domyślne, więc zmienisz atrybuty takiego obiektu lub danych Struktura. W ten sposób nie musisz tworzyć 20 wersji tej samej metody, aby za każdym razem dodawać pojedynczy parametr.
dukeofgaming