poniedziałek, 29 października 2012

różne typy zwracane?

ach ten pehape...

Jedyną rzeczą, której naprawdę nie mogę przeboleć w PHP, to fakt, że nie posiada on typowania. Oczywiście można "umówić się" z innymi programistami w zespole, że "typujemy" tzn. w komentarzach określamy typ przyjmowany i zwracany i się tego trzymamy, ale jest to jedynie obejście problemu, a nie jego rozwiązanie.
Dobra, niestety świat nie jest idealny i musimy jakoś z tym żyć. W dodatku, jeżeli kod jest rzeczywiście obiektowy, to w większości przypadków typowania rzeczywiście można używać na tyle często, że zapomina się o problemie.

Jednak tylko do czasu...

deklaracja metody

Deklaracja metody to jej nazwa oraz liczba i typ parametrów. Typ zwracany nie jest brany pod uwagę, ponieważ nie da się go określić w momencie wywoływania metody. Dlatego też jest możliwe przeładowanie metod, czyli zmiana parametrów (ich typów bądź liczby) i pozostawienie tej samej nazwy, jednak nie można pozostawić tych samych parametrów i nazwy, a zmienić typu zwracanego.
Pomiędzy metodami przeładowanymi nie ma obowiązku zachowywać typu zwracanego, ponieważ na podstawie parametrów można jednoznacznie określić, która metoda zostanie wywołana, a co za tym idzie, jaki będzie typ zwracany.

Przykład takich metod poniżej (tym razem w Javie, bo w PHPie niestety przeładowania metod brak):
public int foo(int valueA, double valueB) {/* code */}
public double foo(double value) {/* code */}
public String foo(String value) {/* code */}

za to nie jest możliwe coś takiego:
public int foo(int value) {/* code */}
public double foo(int value) {/* code */}

I wbrew wszystkiemu nie jest to tylko widzi mi się twórców języka, ale ma spory sens - przy próbie wywołania metody program, by zgłupiał, ponieważ nie wiedziałby, którą z nich powinien odpalić. Nazwa taka sama, parametry te same. Może rzucić monetą?

kiedy używać różnych typów zwracanych

Jeżeli piszesz w PHP niestety żadne IDE nie zadba o to, abyś nie mieszał w typach zwracanych. Wszystko spoczywa na Twoich barkach, to Ty jesteś osobą, która musi zadbać o to, aby wszystko trzymało się kupy i było jasne.

Tak, w PHPie nie ma możliwości przeładowania metod, co nie zmienia faktu, że niekiedy by się to bardzo przydało:) Co możemy w takich sytuacjach zrobić? Niestety tylko tyle, że zasymulujemy takie działanie, co (wzorując się na pierwszym kodzie) mogłoby wyglądać mniej więcej tak:
/**
 * @param int|double|String $valueA
 * @param double $valueA [optional] Default set to null.
 *               Is required if $valueA is int. In other case it's useless.
 * @return
 *     int     - if both params are passed (first is int)
 *     double  - if first param is double
 *     String  - if first param is String
 */
public foo($valueA, $valueB = NULL) 
{
  if (is_null($valueB))
  {
    return is_int($valueA) ? $this->fooForInt($valueA) : $this->fooForString($valueB);
  }
  else
  {
    return $this->fooForTwoParams($valueA, $valueB);
  }
}

Oczywiście należy dodać jeszcze wyrzucanie wyjątków w przypadku przekazania błędnych typów. Ciało metody nie musi koniecznie wyglądać w ten sposób, przykład, szczerze mówiąc, jest przekoloryzowany, ale tylko dlatego, aby tym jaśniej przedstawić, o co chodzi.
Ważne jest, aby wynik zwracany był dokładnie opisany oraz zależny od parametrów przekazywanych, czyli nie może mieć miejsca sytuacja:
/**
 * @return
 *     int     - if friday
 *     double  - if weekend
 *     String  - if I'm in bad mood
 */
public foo($valueA, $valueB = NULL) {/* code */}

dlaczego unikać typów, których nie możemy przewidzieć

Odpowiedź na to pytanie jest zawarta w jego treści - ponieważ nie możemy przewidzieć rezultatu takiej operacji, a to wymusza na nas obsługę tego w każdym miejscu, w którym jest wywoływana metoda. A za tym idzie? Dużo większa ilość kodu oraz dużo więcej problemów w przypadku zmiany implementacji metody.