Подтвердить что ты не робот

Почему PHP Trait не может реализовать интерфейсы?

Мне интересно, почему PHP Trait (PHP 5.4) не может реализовать интерфейсы.

Обновление от user1460043 answer = > ... не может потребовать класс, который использует его для реализации определенного интерфейса

Я понимаю, что это может быть очевидно, потому что люди могут подумать, что если Class A использует Trait T, который реализует interface I, чем Class A должен использовать interface I непрямо (и это не так, потому что Class A может переименовывать методы trait).

В моем случае моя черта - это вызов методов из интерфейса, который реализует класс, использующий свойство.

Эта черта на самом деле является реализацией некоторых методов интерфейса. Итак, я хочу "разработать" в коде, что каждый класс, который хочет использовать мой признак, должен реализовать интерфейс. Это позволило бы Trait использовать методы класса, определенные интерфейсом, и быть уверенным, что они существуют в классе.

4b9b3361

Ответ 1

Действительно короткая версия проще, потому что вы не можете. Это не то, как работают Черты.

Когда вы пишете use SomeTrait; в PHP, вы (эффективно) говорите компилятору, чтобы скопировать и вставить код из Trait в класс, в котором он используется.

Поскольку use SomeTrait; находится внутри класса, он не может добавить implements SomeInterface в класс, потому что это должно быть вне класса.

"почему в PHP нет типов типов?"

Потому что они не могут быть созданы. Черты - это всего лишь языковая конструкция (рассказывающая компилятору, чтобы скопировать и вставить код признака в этот класс), в отличие от объекта или типа, на который может ссылаться ваш код.

Итак, я хочу "спроектировать" в коде, который каждый класс, который хочет использовать моя черта должна реализовать интерфейс.

Это можно применить с использованием абстрактного класса к 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();

Однако - если вам нужно обеспечить, чтобы какой-либо класс, который использует Trait, имеет конкретный метод, я думаю, вы можете использовать черты, в которых вы должны были быть абстрактными классами в первую очередь.

Или у вас есть логика неправильная. Вы должны требовать, чтобы классы, реализующие интерфейсы, имели определенные функции, а не то, что если у них есть определенные функции, они должны объявить себя как реализацию интерфейса.

Edit

На самом деле вы можете определить абстрактные функции внутри черт, чтобы заставить класс реализовать этот метод. например.

trait LoggerTrait {

    public function debug($message, array $context = array()) {
        $this->log('debug', $message, $context);
    }

    abstract public function log($level, $message, array $context = array());
}

Однако это все еще не позволяет вам реализовать интерфейс в признаке и по-прежнему пахнет плохим дизайном, поскольку интерфейсы намного лучше, чем черты при определении контракта, который должен выполнять класс.

Ответ 2

Там RFC: Черты с интерфейсами предлагает следующее добавить к языку:

trait SearchItem implements SearchItemInterface
{
    ...
}

Методы, требуемые интерфейсом, могут быть реализованы по признаку или объявлены как абстрактные, и в этом случае ожидается, что класс, который использует этот признак, реализует его.

Эта функция в настоящее время не поддерживается языком, но она находится на рассмотрении (текущее состояние RFC: В разделе Обсуждение).

Ответ 3

[...], чтобы "создать" в коде, что каждый класс, который хочет использовать мою черту должны реализовать интерфейс. Это позволит Trait использовать методы класса определенные интерфейсом, и убедитесь, что они существуют в классе.

Это звучит очень разумно, и я бы не сказал, что в вашем дизайне должно быть что-то не так. С этой идеей были предложены черты, см. Второй пункт здесь:

  • Характеристика предоставляет набор методов, реализующих поведение.
  • Для характеристики требуется набор методов, которые служат параметрами предоставляемого поведения.
  • [...]

Schärli et al., Traits: Composable Units of Behavior, ECOOP2003, LNCS 2743, pp. 248-274, Springer Verlag, 2003, Страница 2

Таким образом, было бы более уместным сказать, что вы хотите, чтобы черта требовала интерфейса, а не "реализула" его.

Я не вижу причин, по которым было бы невозможно иметь эту "черту" (ее потребительские классы для реализации) функцию интерфейса в PHP, но в настоящее время она, кажется, отсутствует.

Как отмечает @Danack в ответе , вы можете использовать абстрактные функции в свойстве, чтобы "потребовать" их от классов, которые используют этот признак. К сожалению, вы не можете сделать это с помощью частных функций.

Ответ 4

Я согласен с ответом @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.
    }
}

Извините английский