Zastanawiam się, dlaczego PHP Trait (PHP 5.4) nie może implementować interfejsów.
Aktualizacja z answer => ... użytkownika1460043 nie może wymagać klasy, która używa jej do implementacji określonego interfejsu
Rozumiem, że może to być oczywiste, ponieważ ludzie mogą pomyśleć, że jeśli a Class A
używa a, Trait T
które implementuje an interface I
, to Class A
powinno implementować się pośrednio interface I
(i to nie jest prawda, ponieważ Class A
można zmienić nazwę metod cech).
W moim przypadku cechą jest wywoływanie metod z interfejsu, które implementuje klasa używająca cechy.
Cecha jest tak naprawdę implementacją niektórych metod interfejsu. Tak więc chcę „zaprojektować” w kodzie, że każda klasa, która chce używać mojej cechy, musi zaimplementować interfejs. Umożliwiłoby to Trait użycie metod klas zdefiniowanych przez interfejs i upewnienie się, że istnieją w klasie.
Odpowiedzi:
Naprawdę krótka wersja jest prostsza, ponieważ nie możesz. Nie tak działają cechy.
Kiedy piszesz
use SomeTrait;
w PHP, mówisz (efektywnie) kompilatorowi, aby skopiował i wkleił kod z cechy do klasy, w której jest używany.Ponieważ element
use SomeTrait;
znajduje się wewnątrz klasy, nie można go dodawaćimplements SomeInterface
do klasy, ponieważ musi znajdować się poza klasą.Ponieważ nie można ich utworzyć. Cechy są tak naprawdę konstrukcją językową (nakazującą kompilatorowi skopiowanie i wklejenie kodu cechy do tej klasy) w przeciwieństwie do obiektu lub typu, do którego może się odwoływać Twój kod.
Można to wymusić, używając klasy abstrakcyjnej do
use
cechy, a następnie rozszerzając z niej klasy.interface SomeInterface{ public function someInterfaceFunction(); } trait SomeTrait { function sayHello(){ echo "Hello my secret is ".static::$secret; } } abstract class AbstractClass implements SomeInterface{ use SomeTrait; } class TestClass extends AbstractClass { static public $secret = 12345; //function someInterfaceFunction(){ //Trying to instantiate this class without this function uncommented will throw an error //Fatal error: Class TestClass contains 1 abstract method and must therefore be //declared abstract or implement the remaining methods (SomeInterface::doSomething) //} } $test = new TestClass(); $test->sayHello();
Jednak - jeśli musisz wymusić, że jakakolwiek klasa, która używa cechy, ma określoną metodę, myślę, że możesz używać cech, w których powinieneś był przede wszystkim klasami abstrakcyjnymi.
Albo że masz złą logikę. Masz wymagać, aby klasy, które implementują interfejsy, miały określone funkcje, a nie to, że jeśli mają pewne funkcje, muszą zadeklarować się jako implementujące interfejs.
Edytować
Właściwie możesz zdefiniować abstrakcyjne funkcje wewnątrz Traits, aby wymusić na klasie implementację metody. na przykład
trait LoggerTrait { public function debug($message, array $context = array()) { $this->log('debug', $message, $context); } abstract public function log($level, $message, array $context = array()); }
Jednak to nadal nie pozwala na zaimplementowanie interfejsu w tej funkcji i nadal pachnie jak zły projekt, ponieważ interfejsy są znacznie lepsze niż cechy w definiowaniu kontraktu, który klasa musi spełnić.
źródło
type
metody ). Czy możesz pomyśleć, jak można to zaimplementować bez cech?Jest RFC: Traits with interfaces sugeruje dodanie do języka:
trait SearchItem implements SearchItemInterface { ... }
Metody wymagane przez interfejs mogą być implementowane przez cechę lub zadeklarowane jako abstrakcyjne, w takim przypadku oczekuje się, że klasa, która używa cechy, implementuje ją.
Ta funkcja nie jest obecnie obsługiwana przez język, ale jest rozważana (obecny stan RFC to: W trakcie dyskusji ).
źródło
Brzmi to bardzo rozsądnie i nie powiedziałbym, że z twoim projektem musi być coś złego. Cechy zostały zasugerowane z myślą o tym pomyśle, zobacz drugi punkt tutaj:
Schärli i in., Traits: Composable Units of Behavior, ECOOP'2003, LNCS 2743, str. 248–274, Springer Verlag, 2003, str. 2
Więc może lepiej byłoby powiedzieć, że chcesz, aby cecha wymagała interfejsu, a nie „implementowała” go.
Nie widzę powodu, dla którego byłoby niemożliwe, aby ta cecha wymagała (jej klas konsumenckich do zaimplementowania) funkcji interfejsu w PHP, ale obecnie wydaje się jej brakować.
Jak zauważa @Danack w swojej odpowiedzi , możesz użyć funkcji abstrakcyjnych w cechy, aby „wymagać” ich od klas, które ją używają. Niestety nie możesz tego zrobić z prywatnymi funkcjami .
źródło
Zgadzam się z odpowiedzią @Danack, jednak trochę ją uzupełnię.
Przychodzi mi do głowy tylko kilka przypadków, w których to, o co prosisz, jest konieczne i jest bardziej widoczne jako problem projektowy niż jako błąd języka. Wyobraź sobie, że jest taki interfejs:
interface Weaponize { public function hasAmmunition(); public function pullTrigger(); public function fire(); public function recharge(); }
Utworzono cechę , która implementuje jedną z funkcji zdefiniowanych w interfejsie, ale w procesie wykorzystuje inne funkcje również zdefiniowane przez interfejs , podatne na błąd, że jeśli klasa, która używa tej funkcji, nie implementuje interfejsu, wszystko nie może pobrać cyngiel
trait Triggerable { public function pullTrigger() { if ($this->hasAmmunition()) { $this->fire(); } } } class Warrior { use Triggerable; }
Łatwym rozwiązaniem jest po prostu wymuszenie na klasie, która używa cechy, aby zaimplementowała również te funkcje:
trait Triggerable { public abstract function hasAmmunition(); public abstract function fire(); public function pullTrigger() { if ($this->hasAmmunition()) { $this->fire(); } } }
Czyli cecha nie jest całkowicie zależna od interfejsu , ale propozycja implementacji jednej z jej funkcji, ponieważ używając cechy klasa będzie wymagała implementacji metod abstrakcyjnych.
Ostateczny projekt
interface Weaponize { public function hasAmmunition(); public function pullTrigger(); public function fire(); public function recharge(); } trait Triggerable { public abstract function hasAmmunition(); public abstract function fire(); public function pullTrigger() { if ($this->hasAmmunition()) { $this->fire(); } } } class Warrior implements Weaponize { use Triggerable; public function hasAmmunition() { // TODO: Implement hasAmmunition() method. } public function fire() { // TODO: Implement fire() method. } public function recharge() { // TODO: Implement recharge() method. } }
Proszę wybaczyć mój angielski
źródło