Me pregunto por qué PHP Trait (PHP 5.4) no puede implementar interfaces.
Actualización de user1460043's answer => ... no puede requerir una clase que la use para implementar una interfaz específica
Entiendo que podría ser obvio, porque la gente podría pensar que si a Class A
está usando a Trait T
que está implementando a interface I
, entonces Class A
debería estar implementando el interface I
indirectamente (y esto no es cierto porque Class A
podría cambiar el nombre de los métodos de rasgos).
En mi caso, mi rasgo es llamar a métodos desde la interfaz que implementa la clase que usa el rasgo.
El rasgo es de hecho una implementación de algunos métodos de la interfaz. Entonces, quiero "diseñar" en el código que cada clase que quiera usar mi rasgo tenga que implementar la interfaz. Eso permitiría al Trait usar métodos de clase definidos por la interfaz y asegurarse de que existan en la clase.
Respuestas:
La versión realmente corta es más simple porque no puedes. No es así como funcionan los rasgos.
Cuando escribe
use SomeTrait;
en PHP, le está diciendo (efectivamente) al compilador que copie y pegue el código del Trait en la clase donde se está utilizando.Debido a que
use SomeTrait;
está dentro de la clase, no se puede agregarimplements SomeInterface
a la clase, porque tiene que estar fuera de la clase.Porque no se pueden crear instancias. Los rasgos son en realidad solo una construcción del lenguaje (que le dice al compilador que copie y pegue el código del rasgo en esta clase) en lugar de un objeto o tipo al que su código puede hacer referencia.
Eso se puede hacer cumplir usando una clase abstracta para
use
el rasgo y luego extendiendo clases desde él.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();
Sin embargo, si necesita hacer cumplir que cualquier clase que use un Rasgo tiene un método en particular, creo que puede estar usando rasgos donde debería haber sido clases abstractas en primer lugar.
O que tienes tu lógica al revés. Debe requerir que las clases que implementen interfaces tengan ciertas funciones, no que si tienen ciertas funciones deban declararse como implementadoras de una interfaz.
Editar
En realidad, puede definir funciones abstractas dentro de Traits para forzar a una clase a implementar el método. p.ej
trait LoggerTrait { public function debug($message, array $context = array()) { $this->log('debug', $message, $context); } abstract public function log($level, $message, array $context = array()); }
Sin embargo, esto todavía no le permite implementar la interfaz en el rasgo, y aún huele como un mal diseño, ya que las interfaces son mucho mejores que los rasgos para definir un contrato que una clase debe cumplir.
fuente
type
método ). ¿Puedes pensar en cómo podría implementarse esto sin rasgos?Hay un RFC: Traits with interfaces sugiere que se agreguen al idioma:
trait SearchItem implements SearchItemInterface { ... }
Los métodos requeridos por la interfaz pueden ser implementados por el rasgo o declarados como abstractos, en cuyo caso se espera que la clase que usa el rasgo lo implemente.
Actualmente, esta característica no es compatible con el idioma, pero se está considerando (el estado actual del RFC es: En discusión ).
fuente
Esto suena muy razonable y no diría que tiene que haber algún problema con su diseño. Se han sugerido rasgos con esta idea en mente, consulte el segundo punto aquí:
Schärli et al, Traits: Composable Units of Behavior, ECOOP'2003, LNCS 2743, págs. 248–274, Springer Verlag, 2003, página 2
Por lo tanto, sería más apropiado decir que desea que un rasgo requiera una interfaz, no "implementarlo".
No veo una razón por la que debería ser imposible tener este "rasgo requiere (sus clases de consumidores para implementar) una función de interfaz" en PHP, pero actualmente parece que falta.
Como señala @Danack en su respuesta , puede usar funciones abstractas en el rasgo para "requerirlas" de las clases que usan el rasgo. Desafortunadamente, no puede hacer esto con funciones privadas .
fuente
Estoy de acuerdo con la respuesta de @Danack, sin embargo la complementaré un poco.
Solo puedo pensar en algunos casos en los que lo que solicitas es necesario y es más evidente como un problema de diseño que como una falla de lenguaje. Solo imagina que hay una interfaz como esta:
interface Weaponize { public function hasAmmunition(); public function pullTrigger(); public function fire(); public function recharge(); }
Se ha creado un rasgo que implementa una de las funciones definidas en la interfaz pero en el proceso usa otras funciones también definidas por la interfaz , propenso al error de que si la clase que usa la característica no implementa la interfaz todo falla al tirar de la desencadenar
trait Triggerable { public function pullTrigger() { if ($this->hasAmmunition()) { $this->fire(); } } } class Warrior { use Triggerable; }
Una solución fácil es simplemente forzar a la clase que usa el rasgo a implementar esas funciones también:
trait Triggerable { public abstract function hasAmmunition(); public abstract function fire(); public function pullTrigger() { if ($this->hasAmmunition()) { $this->fire(); } } }
Entonces, el rasgo no depende completamente de la interfaz , sino una propuesta para implementar una de sus funciones, ya que al usar el rasgo la clase demandará la implementación de los métodos abstractos.
Diseño final
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. } }
Por favor disculpe mi ingles
fuente