Мне интересно, почему PHP Trait (PHP 5.4) не может реализовывать интерфейсы.
Обновление от user1460043 answer => ... не может требовать класс, который использует его для реализации определенного интерфейса
Я понимаю, что это могло быть очевидным, потому что люди могли подумать, что если a Class A
использует a, Trait T
который реализует a interface I
, то он Class A
должен реализовывать a не interface I
напрямую (и это неверно, потому что Class A
можно переименовать методы черт).
В моем случае моя черта - это вызов методов из интерфейса, который реализует класс, использующий эту черту.
Эта черта фактически является реализацией некоторых методов интерфейса. Итак, я хочу «спроектировать» код, чтобы каждый класс, который хочет использовать мою черту, должен реализовывать интерфейс. Это позволит Trait использовать методы класса, определенные интерфейсом, и быть уверенным, что они существуют в классе.
Ответы:
Действительно короткая версия проще, потому что вы не можете. Так работают черты характера.
Когда вы пишете
use SomeTrait;
на PHP, вы (эффективно) говорите компилятору скопировать и вставить код из Trait в класс, где он используется.Поскольку он
use SomeTrait;
находится внутри класса, он не может добавлять егоimplements SomeInterface
к классу, потому что он должен находиться вне класса.Потому что они не могут быть созданы. На самом деле черты - это просто языковая конструкция ( указывающая компилятору скопировать и вставить код черты в этот класс), а не объект или тип, на которые может ссылаться ваш код.
Этого можно добиться с помощью абстрактного класса для
use
признака, а затем расширения классов из него.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();
Однако - если вам действительно нужно обеспечить, чтобы любой класс, который использует черту, имел определенный метод, я думаю, вы можете использовать черты там, где вы должны были быть абстрактными классами в первую очередь.
Или что у вас неверная логика. Вы должны требовать, чтобы классы, реализующие интерфейсы, имели определенные функции, а не то, что если у них есть определенные функции, они должны объявить себя реализующими интерфейс.
редактировать
Фактически вы можете определить абстрактные функции внутри Traits, чтобы заставить класс реализовать метод. например
trait LoggerTrait { public function debug($message, array $context = array()) { $this->log('debug', $message, $context); } abstract public function log($level, $message, array $context = array()); }
Однако это по-прежнему не позволяет реализовать интерфейс в трейте и по-прежнему пахнет плохим дизайном, поскольку интерфейсы намного лучше, чем трейты при определении контракта, который должен выполнить класс.
источник
type
метод ). Можете ли вы представить, как это можно реализовать без черт?Есть RFC: Traits with interfaces предлагает добавить в язык следующее:
trait SearchItem implements SearchItemInterface { ... }
Методы, требуемые интерфейсом, могут быть либо реализованы с помощью признака, либо объявлены как абстрактные, и в этом случае ожидается, что класс, использующий признак, реализует его.
Эта функция в настоящее время не поддерживается языком, но находится на рассмотрении (текущий статус RFC: в стадии обсуждения ).
источник
Это звучит очень разумно, и я бы не сказал, что с вашим дизайном что-то не так. С учетом этой идеи были предложены черты, см. Второй пункт здесь:
Шерли и др., Черты: составные единицы поведения, ECOOP'2003, LNCS 2743, стр. 248–274, Springer Verlag, 2003, стр. 2
Так что, возможно, было бы правильнее сказать, что вы хотите, чтобы черта требовала интерфейса, а не «реализовывала» его.
Я не вижу причин, по которым эта черта «требует (для реализации ее потребительских классов) интерфейсной функции» в PHP должна быть невозможной, но в настоящее время она, похоже, отсутствует.
Как отмечает @Danack в своем ответе , вы можете использовать абстрактные функции в трейте, чтобы «требовать» их от классов, которые используют трейт. К сожалению, вы не можете этого сделать с частными функциями .
источник
Я согласен с ответом @Danack, но немного дополню его.
Я могу вспомнить лишь несколько случаев, когда то, что вы запрашиваете, необходимо и более очевидно как проблема дизайна, чем как языковой сбой. Представьте себе, что есть такой интерфейс:
interface Weaponize { public function hasAmmunition(); public function pullTrigger(); public function fire(); public function recharge(); }
Был создан признак , который реализует одну из функций, определенных в интерфейсе, но в процессе использует другие функции, также определенные в интерфейсе , подверженные ошибкам, так как если класс, который использует эту функцию, не реализует интерфейс, все не сможет извлечь спусковой крючок
trait Triggerable { public function pullTrigger() { if ($this->hasAmmunition()) { $this->fire(); } } } class Warrior { use Triggerable; }
Простое решение - просто заставить класс, который использует трейт, также реализовать эти функции:
trait Triggerable { public abstract function hasAmmunition(); public abstract function fire(); public function pullTrigger() { if ($this->hasAmmunition()) { $this->fire(); } } }
Таким образом, признак не полностью зависит от интерфейса , а является предложением реализовать одну из его функций, поскольку при использовании признака класс потребует реализации абстрактных методов.
Окончательный дизайн
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. } }
Прошу прощения за мой английский
источник