2012-11-16 8 views
38

Wiem, że istnieje kilka podobnych pytań tutaj w StackOverflow, takich jak this question.Dlaczego przesłanianie parametrów metody stanowi naruszenie ścisłych standardów w PHP?

Dlaczego przesłanianie parametrów metody stanowi naruszenie ścisłych standardów w PHP? Na przykład:

class Foo 
{ 
    public function bar(Array $bar){} 
} 

class Baz extends Foo 
{ 
    public function bar($bar) {} 
} 

surowe normy: Deklaracja Baz :: bar() powinny być zgodne z że Foo :: Bar()

w innych językach programowania OOP można . Dlaczego jest źle w PHP?

Odpowiedz

46

W OOP, SOLID oznacza jednolitego odpowiedzialność, Open-zamknięte , Podstawienie Liskov, segregacja interfejsu i inwersja zależności:.

Liskov substitution zasada stwierdza, że ​​w programie komputerowym, jeśli Bar jest podtypem Foo, to obiekty typu Foo można zastąpić obiektów typu Bar bez zmiany któregokolwiek z pożądanych właściwościach tego programu (poprawność, wykonane zadanie itp.).

W językach programowania napisanych silnie na maszynie, po nadpisaniu metody Foo, jeśli zmienisz podpis na pasku, faktycznie jesteś przeciążony przeciążając, ponieważ oryginalna metoda i nowa metoda są dostępne z różnymi sygnaturami. Ponieważ PHP jest słabo napisane, nie można tego osiągnąć, ponieważ kompilator nie może wiedzieć, którą z metod faktycznie wywołuje. (stąd powód, dla którego nie możesz mieć 2 metod o tej samej nazwie, nawet jeśli ich podpisy są różne).

Aby uniknąć naruszenia zasady Liskov Substitution, wydawane jest ostrzeżenie standardowe, informujące programistę, że coś może się zepsuć z powodu zmiany sygnatury metody w klasie potomnej.

+20

Nie zgadzam się z dwiema rzeczami tutaj: "jeśli Bar jest podtypem Foo, obiekty typu Foo mogą być zastąpione obiektami typu Bar (i na odwrót)". Część vice versa nie trzyma; nie spodziewasz się używać superklasy w żadnym miejscu, w którym używana jest podlaska. Inna sprawa dotyczy LSP: jeśli parametry są sprzeczne, to typy są respektowane. Gdyby PHP było czystym OO, wtedy nie byłoby problemu, ponieważ Array byłby węższy niż Object (zakładając, że żaden typ w deklaracji nie oznacza żadnego obiektu). Tak więc implementacja bar() byłaby bardziej ogólna w Baz, dzięki czemu pasowałaby do LSP –

+1

@ AndrésFortier. Na odwrót miałem na myśli, że w zakresie Foo, Foo i Bar powinny być wymienne. Jednak jeśli chodzi o drugi temat, zgadzam się z tobą. Przykład podany przez Kaern nie narusza LSP, ponieważ Baz :: bar() może bezpiecznie zastąpić Foo :: bar(). – Tivie

+2

Bardzo dobre wyjaśnienie, dzięki. Więc dlaczego mogę złamać zasadę Liskov w konstruktorze? –

5

Można nadpisać parametry, ale podpis powinien być zgodny. Jeśli odłożyłeś Array przed $bar, nie byłoby problemu.

Na przykład, jeśli dodałeś dodatkowy parametr, nie byłoby problemu, pod warunkiem, że pierwszy parametr miałby ten sam typ podpowiedzi. Jest to dobra praktyka w każdym języku.

+0

Ale dlaczego podpis powinien być zgodny? W Javie mogę zmienić sygnaturę –

+1

@KaernStone, Możesz również w PHP, ale nie powinieneś. Przypuśćmy, że masz swoją podklasę 'Foo' zwaną' Baz', a jakiś inny kod chce współdziałać z instancją 'Baz', jakby była' Foo'. Powinien być w stanie to zrobić bez modyfikacji, ponieważ 'Baz' ** to ** a' Foo'. PHP pozwoli ci złamać tę zasadę, ale nie powinieneś, chyba że masz ku temu dobry powód. – Brad

+1

@Brad, zobacz moje komentarze w odpowiedzi Tivie. Jeśli nie wpisanie parametru w parametrze oznacza, że ​​metoda może przyjąć dowolny obiekt, oznacza to, że są one kontrawariantne, a zatem Baz ** to-a ** Foo. Nie powinno być problemu z punktu widzenia systemu pisania. Proszę zobaczyć [tutaj] (http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29) –

2

Ponieważ zadeklarowane Foo że $bar powinny być typu array, natomiast w rozciągającej Bar, $bar „s typ nie jest zadeklarowana.

To nie jest błąd, to ostrzeżenie. Powinieneś uczynić definicję metody zgodną z oryginalną, podstawową klasą. Można jednak bezpiecznie zignorować, jeśli wiesz co robisz (i tylkojeśli wiesz co robisz !!!)

+0

tak, wiem. Ale dlaczego powinienem uczynić definicję metody zgodną z oryginalną, podstawową klasą? –

+1

@KaernStone: Ponieważ inne klasy/metody/aplikacje polegają na tym oryginalnym interfejsie, a jeśli go zmienisz, możesz złamać kompatybilność. –

16

Wiem, że spóźniam się na imprezę, ale odpowiedzi nie wyjaśniają rzeczywistego problemu.

Problem polega na tym, że PHP nie obsługuje przeciążania funkcji/metod. Trudno byłoby wspierać przeciążanie funkcji w języku bez typu.

Podpowiedź pomaga. ale w PHP jest bardzo ograniczony. Nie pewny dlaczego. Na przykład nie możesz wskazać, że zmienna to int lub Boolean, ale tablica jest w porządku. Domyśl!

Inne języki zorientowane obiektowo realizują to przy użyciu funkcji przeciążania. Co oznacza, że ​​podpis funkcji jest oczywiście inny.

Tak na przykład, jeśli następuje to możliwe, że nie będzie miał problemu

class Foo 
{ 
    public function bar(Array $bar){ 
     echo "Foo::bar"; 
    } 
} 

class Baz extends Foo 
{ 
    public function bar(int $bar) { 
     echo "Baz::bar"; 
    } 
} 


$foo = new Baz(); 
$bar = new Baz(); 
$ar = array(); 
$i = 100; 

$foo->bar($ar); 
$bar->bar((int)$i); 

would output 

Foo::bar 
Baz::bar 

Oczywiście, gdy przyszło do konstruktorów programiści php sobie sprawę, że trzeba go wdrożyć, podoba czy nie! Po prostu tłumią błąd lub nie podnoszą go w pierwszym przypadku.

Co jest głupie.

Kiedyś znajomy PHP zaimplementował obiekty tylko jako sposób implementacji przestrzeni nazw. Teraz nie jestem aż tak krytyczny, ale niektóre z podjętych decyzji mają tendencję do popierania tej teorii.

Zawsze mam włączone maksymalne ostrzeżenia podczas tworzenia kodu, nigdy nie pozwolę, aby ostrzeżenie przeszło bez zrozumienia, co to oznacza i jakie są tego konsekwencje. Osobiście nie obchodzi mnie to ostrzeżenie. Wiem, co chcę zrobić i PHP nie robi tego dobrze. Przyszedłem tutaj, szukając sposobu, aby go selektywnie stłumić. Jeszcze nie znalazłem sposobu.

Więc zatrzymam to ostrzeżenie i stłumię je. Wstyd, muszę to zrobić. ale ja jestem surowy w odniesieniu do STRICT.

+2

W odniesieniu do konstruktorów, w PHP 5.4+ sygnatury metod muszą być kompatybilne podczas rozszerzania klasy _abstract_, w przeciwnym razie wystąpi błąd krytyczny. Trochę dziwne, że przed PHP 5.4 nie było nawet ostrzeżenia E_STRICT, a jeśli klasa bazowa nie jest abstrakcyjna, to nie ma żadnych komunikatów niezależnie od wersji PHP. – MrWhite

+0

Dziękuję, dziękuję, dziękuję. To jest pierwsze i jedyne miejsce, w którym widziałem słowo PHP celowo tłumiące błędy w konstruktorze, bardzo nieprzyjemne. Jest to naruszenie LSP, ponieważ dopuszczenie argumentu podtypu w konstruktorze jest przypadkiem wzmocnienia warunków wstępnych. Argumenty metody powinny być zawsze niezmienne lub sprzeczne, a nie kowariantne. W rzeczywistości, w PHP, uważam, że jest to jedyny scenariusz, w którym możliwe jest cokolwiek innego niż niezmienność wpisanych argumentów w nadpisanych metodach. –

Powiązane problemy