Poprawne zastosowanie przeciążania metod

Poprawne zastosowanie przeciążania metod
MB
  • Rejestracja:ponad rok
  • Ostatnio:ponad rok
  • Postów:1
0

Czołem!

Grzebiąc na forum znalazłem tylko jeden wątek na ten temat, ale troszkę inny i odpowiedzi mnie nie satysfacjonowały. Uczę się Javy i właśnie bawię się biblioteką Javax mail.
Chciałbym napisać dwie metody wysyłające wiadomość, z czego jedna przyjmowałaby jeden argument więcej - listę osób do cc, pierwsza metoda wysyłała by tylko do adresatów bez cc.

screenshot-20231025004013.png

Czy przeciążanie metod w tym przypadku jest dobrym pomysłem? Czy da się tu jakoś uniknąć powielania kodu?

Do głowy przychodzi mi jeszcze opcja stworzenia metody, która przyjmowałaby maksymalną ilość argumentów, a wywołując ją w argumencie ccList przekazywałbym null, a w metodzie sprawdzałbym jak poniżej czy przekazany argument jest nullem i według tego wywoływał odpowiednią metodę.
Wydaje mi się to jednak kompletnie "niepoprawne", aby null świadomie przekazywać jako argument.

screenshot-20231025004529.png

Dzięki z góry za odpowiedź. Nie wiem, czy jako początkujący nie za bardzo zagłębiam się w szczegóły?

Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Laska, z Polski
  • Postów:10068
2
mis_barei napisał(a):

Czy przeciążanie metod w tym przypadku jest dobrym pomysłem? Czy da się tu jakoś uniknąć powielania kodu?

Przeciążanie to nic innego jak dwie osobne funkcje, które po prostu mają taką samą nazwę. Nie dają niczego dodatkowego, czego nie mógłbyś załatwić po prostu dwoma metodami.

Więc to nie jest tak że "przeciążanie metody" pozwala uniknąć powielania kodu, tylko po prostu metody pozwalają na code re-use - a to czy mają taką samą czy inną nazwę to szczegół.

W skrócie tak - osobne funkcje z osobnymi sygnaturami to dobry pomysł. To czy mają mieć taką samą czy inną nazwę to kwestia preferencji czy gustu IMO.

Do głowy przychodzi mi jeszcze opcja stworzenia metody, która przyjmowałaby maksymalną ilość argumentów, a wywołując ją w argumencie ccList przekazywałbym null, a w metodzie sprawdzałbym jak poniżej czy przekazany argument jest nullem i według tego wywoływał odpowiednią metodę.

To na pewno jest gorszy sposób.

edytowany 1x, ostatnio: Riddle
tefu
  • Rejestracja:około 2 lata
  • Ostatnio:około 2 godziny
  • Postów:474
0

Moim zdaniem nie ma większego znaczenia. Ja skłaniałbym się ku wersji dwóch osobnych metod. To czy będziesz tu coś nadpisywać to nie ma już moim zdaniem takiego dużego znaczenia.

W drugim przypadku mógłbyś przekazywać wszystkie argumenty a ccList opakować w Optional ale wydaje mi się przekombinowane. O powielaniu kodu możemy mówić gdybyś tych metod miał faktycznie z 10. Tutaj masz dwie i dodatkowy argument ccList nie jest tam zbędny.

MM
  • Rejestracja:około 8 lat
  • Ostatnio:2 dni
  • Postów:12
0

Ja bym zostawił te 2 metody, z czego obydwie wołałyby tą samą prywatną metodę, z tym że jedna by ją wołała z nullem jako ccList (albo może lepiej wrzucić tam pustą listę, a nie nulla?), a sama obsługa tego nulla byłaby właśnie w tej prywatnej metodzie. Dzięki temu masz przejrzyste publiczne API, a szczegóły implementacyjne w prywatnej metodzie.

Grzyboo
  • Rejestracja:ponad 9 lat
  • Ostatnio:4 miesiące
  • Postów:206
4
MckMaciek napisał(a):

Ja bym zostawił te 2 metody, z czego obydwie wołałyby tą samą prywatną metodę, z tym że jedna by ją wołała z nullem jako ccList (albo może lepiej wrzucić tam pustą listę, a nie nulla?), a sama obsługa tego nulla byłaby właśnie w tej prywatnej metodzie. Dzięki temu masz przejrzyste publiczne API, a szczegóły implementacyjne w prywatnej metodzie.

Ale po co używać tej patologii zwanej nullami i jakiegoś ifowania? Niech stworzy prywatną metodę przyjmującą jeden parametr Message message, a 2 przeciężone metody niech odpowiadają za zbudowanie message z lub bez ccList

obscurity
  • Rejestracja:około 6 lat
  • Ostatnio:około 15 godzin
2

Nie wklejaj kodu jako obrazki.
A jak jest przeciążona metoda prepareMessage? Nie możesz tam przekazać nulla czy pustej listy?
Moim zdaniem możesz przeciążyć jeśli chcesz i po prostu wywołać jedną metodę z drugiej:

Kopiuj
public void sendMessage(String messageText, String subject, String recipientsList, String ccList) {
  try {
    Session session = createSession();
    Message message = prepareMessage(session, messageText, subject, recipientsList, ccList);
    Transport.send(message);
    copyMessageToSentItemsFoIder(message);
  catch (Exception e) {
    throw new IllegalStateException(e);
  }
}

public void sendMessage(String messageText, String subject, String recipientsList) {
  sendMessage(messageText, subject, recipientsList, "");
}

kod skopiowany z obrazka powertoolsami więc mogą być błędy.
Raczej unikaj powtarzania większości kodu w dwóch metodach bo jak będziesz chciał coś zmienić to musisz to robić w wielu miejscach i łatwo o błędy.
Jeśli nie chcesz ifologii w środku i nullów to wydziel wspólną logikę i na przykład tak jak wyżej sugerował @Grzyboo prepareMessage wywołuj na zewnątrz a metoda niech tylko zajmie się wysyłaniem. Będziesz miał bardziej elastyczny kod i separację logiki. Co w przypadku jak w przyszłości będziesz chciał wysłać maila z załącznikiem? A potem z załącznikiem i CC i z załącznikiem bez CC i może jeszcze bcc albo bez, będziesz tworzył 50 metod na wszystkie warianty?


"A car won't take your job, another horse driving a car will." - Horse influencer, 1910
edytowany 2x, ostatnio: obscurity
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Laska, z Polski
  • Postów:10068
0
obscurity napisał(a):.

Jeśli nie chcesz ifologii w środku i nullów to wydziel wspólną logikę i na przykład tak jak wyżej sugerował @Grzyboo prepareMessage wywołuj na zewnątrz a metoda niech tylko zajmie się wysyłaniem.

To jest bardzo dobra rada.

Będziesz miał bardziej elastyczny kod i separację logiki.

A to z kolei bardzo wartościowe rzeczy, z pewnością należałoby to dodać.

Co w przypadku jak w przyszłości będziesz chciał wysłać maila z załącznikiem? A potem z załącznikiem i CC i z załącznikiem bez CC i może jeszcze bcc albo bez, będziesz tworzył 50 metod na wszystkie warianty?

Ale to jest niemądre gdybanie. YAGNI.

@obscurity: Zamiast tego należałoby to ubrać w słowa "Rozszerzalność kodu ma wiele zalet - jednym ze sposobów na wytwarzanie aplikacji szybko, jest nie stawianie sobie kłód pod nogi, a taka ifologia w nierozszerzalnych funkcjach to jedna z takich kłód. Jeśli przypadkiem miałbyś dodać kolejną funkcjonalność do swojej funkcji (ciężko to przewidzieć, ale może to być np dodanie kolejnych adresatów, dodanie CC albo dodanie zalącznika - albo cokolwiek innego, jakiekolwiek inne wymaganie, którego teraz nie przewidzisz). Dodatkowo, sama praca z taką funkcją jest dużo szybsza, gdybyś chciał ją zrefaktorować, przenieść, reużyć".

edytowany 3x, ostatnio: Riddle
Zobacz pozostałe 6 komentarzy
obscurity
To był tylko przykład na to do czego brak "elastyczności" może prowadzić. Trochę gdybanie a trochę mówię z doświadczenia bo miałem styczność z projektem gdzie był podobny kod i co jakiś czas były potrzebne nowe warianty i ludzie oczywiście tylko dodawali nowe metody. 50 to była hiperbola ale było tych wariantów 8-9, może więcej i nikt tego w ramach swojego taska nie zrefaktorował bo łatwiej dodać nowy wariant niż zmieniać kod w kilkunastu miejscach plus testy. "Ktoś zrefaktoruje potem w innym tasku", jeszcze nie widziałem żeby się to zdarzyło, zawsze są inne priorytety.
Riddle
Nie ma znaczenia czy to była hiperbola - nawet 1 metoda jest tutaj złym argumentem. Argument z tym że "muszę zrefaktorować teraz, bo później nikt nie refaktoruje" też nie jest adekwatny. Nie robi się refaktor "na później". Jeśli się obawiasz że musisz zrobić teraz, bo w późniejszym tajsku nikt nie zrobi jak jak opisałeś, to masz dużo poważniejszy problem - mianowicie czemu Twój team nie robi refaktora kiedy powinien. Poza tym, Twoje dwie pierwsze rady były jaknajbardziej wporzo - co zauważyłem. Ale ostatni, już niestety nie.
obscurity
ok, starałem się tylko nakreślić po co miałby to robić. Jak się jest początkującym to dociera bardzo dużo rad, praktyk itp ale nikt nie tłumaczy po co tak robić i wydaje się to tylko dodatkowym narzutem. No a z refaktoryzacją w korpo tak trochę jest, jak nie poprawisz kodu do końca taska to już zostanie praktycznie zabetonowany. Zmiana tego później wymaga zgód, testów na różnych środowiskach, zaangażowania kupy ludzi, chyba że zrobisz to w ramach innego taska ale wtedy powstają pytania czemu poprzednia podobna zmiana została zrobiona w godzinę a ty się z tym grzebiesz.
Riddle
wtedy powstają pytania czemu poprzednia podobna zmiana została zrobiona w godzinę a ty się z tym grzebiesz czyli refaktor w tym samym tasku miałby zająć na tyle krótko że trzeba to zrobić od razu (nawet jeśli nie do końca wiadomo czy potrzeba), ale jak już w następnym to będziesz posądzony o "grzebanie się"?
Riddle
@obscurity: Dopisałem do posta przykład jak ja wyraziłbym to co chcesz powiedzieć - tak żeby nie sugerować początkującym łamanie YAGNI. Bo tak jak się zgadzam z twoimi dwoma pierwsyzmi argumentami, o ifologii i rozszerzalności - tak całkowicie nie zgadzam się z pomysłem robienia czegoś na przyszłość. Myślę że sugerowanie takiego myślenia nowicjuszom to bardzo zła rada - a to co opisujesz "że trzeba teraz, bo jak nie to w korpo już nigdy bo beton i korpo złe", to jest słabe samo w sobie.
piotrpo
  • Rejestracja:ponad 7 lat
  • Ostatnio:6 dni
  • Postów:3277
2
mis_barei napisał(a):

Do głowy przychodzi mi jeszcze opcja stworzenia metody, która przyjmowałaby maksymalną ilość argumentów, a wywołując ją w argumencie ccList przekazywałbym null, a w metodzie sprawdzałbym jak poniżej czy przekazany argument jest nullem i według tego wywoływał odpowiednią metodę.
Wydaje mi się to jednak kompletnie "niepoprawne", aby null świadomie przekazywać jako argument.

W java to jest bardzo niepoprawne podejście. Sygnatura metody powinna jasno wskazywać które parametry są wymagane, a które opcjonalne. W Java nie ma takiej możliwości, więc należy unikać. W językach, które mają parametry nazwane + null safety IMO jest to dopuszczalny pomysł:

Kopiuj
fun sendMessage(subject:String, to: List<String>, cc: List?<String>)

W Java zrobiłbym przeciążenie "teleskopowe", czyli implementujesz jedną metodę, tę z największą liczbą parametrów, do niej dorabiasz metody z domyślnymi wartościami tych parametrów jak w przykładzie @obscurity

Jeżeli pojawi się potrzeba dodania kolejnego parametru, masz o tyle prosto, że dodajesz ten parametr + dodajesz kolejne przeciążenie tej metody, czyli zmiany będą ograniczone do jednego miejsca.

Kopiuj
public void sendMessage(String messageText, String subject, String recipientsList, String ccList) {
  //jakaś tam implementacja
}

public void sendMessage(String messageText, String subject, String recipientsList) {
  sendMessage(messageText, subject, recipientsList, "");
}

i po refaktoryzacji z dodaniem parametru z załącznikami:

Kopiuj
public void sendMessage(String messageText, String subject, String recipientsList, String ccList, Attachement attachment) {
  //jakaś tam implementacja
}

public void sendMessage(String messageText, String subject, String recipientsList, String ccList) {
  sendMessage(messageText, subject, recipientsList, ccList, null);
}

public void sendMessage(String messageText, String subject, String recipientsList) {
  sendMessage(messageText, subject, recipientsList, "");
}
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:minuta
  • Postów:2370
4

Dużo tych sendMessage. @Grzyboo opisał dobre rozwiązanie. Rozważył bym wprowadzenie MessageBuildera, tak by dać klientowi możliwość zbudowania takiego Message jakiego potrzebuje.
Message.builder().to(...).subject(...).cc(...).cc(...).bcc(...).attachment(...).attachment(...).validate().build();

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)