Otrzymałem dzisiaj recenzję kodu od starszego programisty z pytaniem: „A propos, jaki jest twój sprzeciw wobec wywoływania funkcji za pomocą instrukcji switch?” Czytałem już w wielu miejscach o tym, jak pompowanie argumentu za pomocą przełączania do metod wywoływania jest złym OOP, nie jest tak rozszerzalne itp. Jednak tak naprawdę nie mogę znaleźć ostatecznej odpowiedzi dla niego. Chciałbym załatwić to raz na zawsze.
Oto nasze sugestie dotyczące konkurującego kodu (php użyty jako przykład, ale może być stosowany bardziej uniwersalnie):
class Switch {
public function go($arg) {
switch ($arg) {
case "one":
echo "one\n";
break;
case "two":
echo "two\n";
break;
case "three":
echo "three\n";
break;
default:
throw new Exception("Unknown call: $arg");
break;
}
}
}
class Oop {
public function go_one() {
echo "one\n";
}
public function go_two() {
echo "two\n";
}
public function go_three() {
echo "three\n";
}
public function __call($_, $__) {
throw new Exception("Unknown call $_ with arguments: " . print_r($__, true));
}
}
Część jego argumentacji brzmiał: „To (metoda zamiany) ma znacznie czystszy sposób obsługi domyślnych przypadków niż to, co masz w ogólnej magicznej metodzie __call ()”.
Nie zgadzam się co do czystości i faktycznie wolę połączenie, ale chciałbym usłyszeć, co mają do powiedzenia inni.
Argumenty, które mogę przedstawić na poparcie Oop
schematu:
- Trochę czystszy pod względem kodu, który musisz napisać (mniej, łatwiej czytać, mniej słów kluczowych do rozważenia)
- Nie wszystkie działania przekazane do jednej metody. Nie ma tutaj dużej różnicy w wykonaniu, ale przynajmniej tekst jest bardziej podzielony na przedziały.
- W tym samym duchu można dodać inną metodę w dowolnym miejscu w klasie zamiast w określonym miejscu.
- Metody mają przestrzeń nazw, co jest miłe.
- Nie ma tu zastosowania, ale rozważ przypadek, w którym
Switch::go()
operowany jest na elemencie, a nie na parametrze. Najpierw musisz zmienić członka, a następnie wywołać metodę. PonieważOop
możesz wywoływać metody niezależnie w dowolnym momencie.
Argumenty, które mogę przedstawić na poparcie Switch
schematu:
- Dla uproszczenia, czystsza metoda radzenia sobie z domyślnym (nieznanym) żądaniem
- Wydaje się mniej magiczny, co może sprawić, że nieznani programiści poczują się bardziej komfortowo
Czy ktoś ma coś do dodania po każdej ze stron? Chciałbym mieć dla niego dobrą odpowiedź.
źródło
Oop
pozwala mieć phpdoc do opisywania każdej metody, które mogą być analizowane przez niektóre IDE (np. NetBeans).Odpowiedzi:
Przełącznik uważany jest za inny niż OOP, ponieważ często polimorfizm może załatwić sprawę.
W twoim przypadku implementacja OOP może być następująca:
źródło
dla tego przykładu:
OK, tylko częściowo żartuję. Argument za / przeciw użyciu instrukcji switch nie może być silnie sformułowany w tak trywialnym przykładzie, ponieważ strona OOP zależy od semantyki, a nie tylko mechanizmu wysyłania .
Instrukcje przełączania często wskazują na brakujące klasy lub klasyfikacje, ale niekoniecznie. Czasami instrukcja switch jest tylko instrukcją switch .
źródło
Być może nie jest to odpowiedź, ale w przypadku kodu nieprzełączającego wydaje się, że byłoby to lepsze dopasowanie:
Dużą częścią układanki do oceny jest to, co dzieje się, gdy trzeba utworzyć
NewSwitch
lubNewOop
. Czy twoi programiści muszą skakać przez obręcze jedną czy drugą metodą? Co się stanie, gdy zmienią się twoje zasady itp.źródło
is_callable
, ale opuściłem call_user_func, ponieważ (nie jest częścią tego pytania), mogą istnieć inne argumenty do przekazania. Ale teraz, kiedy to przeszło na programistów, zdecydowanie zgadzam się, że odpowiedź @ Andrei jest znacznie lepsza :)Zamiast po prostu wstawiać kod wykonawczy do funkcji, zaimplementuj pełny wzorzec poleceń i umieść każdy z nich we własnej klasie, która implementuje wspólny interfejs. Ta metoda pozwala używać IOC / DI do łączenia różnych „przypadków” w klasie i pozwala z łatwością dodawać i usuwać przypadki z kodu w miarę upływu czasu. Daje także sporo kodu, który nie narusza zasad programowania SOLID.
źródło
Myślę, że przykład jest zły. Jeśli istnieje funkcja go (), która akceptuje argument $ gdzie, jeśli jest całkowicie poprawne, użyj przełącznika w funkcji. Posiadanie pojedynczej funkcji go () ułatwia modyfikowanie zachowania wszystkich scenariuszy go_where (). Dodatkowo zachowasz interfejs klasy - jeśli używasz różnych metod, modyfikujesz interfejs klasy z każdym nowym miejscem docelowym.
Właściwie przełącznika nie należy zastępować zestawem metod, ale zestawem klas - jest to polimorfizm. Następnie miejsce docelowe będzie obsługiwane przez każdą podklasę, a dla wszystkich będzie jedna metoda go (). Zastąpienie warunkowe polimorfizmem jest jednym z podstawowych refaktoryzacji opisanych przez Martina Fowlera. Ale możesz nie potrzebować polimorfizmu, a zmiana jest właściwą drogą.
źródło
go()
, może to łatwo naruszyć zasadę substytucji Liskowa. Jeśli dodajesz więcej funkcjonalnościgo()
, ty nie chcesz zmienić interfejs miarę Jestem zainteresowaną.