Heim  >  Fragen und Antworten  >  Hauptteil

Wie PHP-Schnittstellen tatsächlich das Verhalten von Klassen verändern

<p>Laut der PHP-Dokumentation ist </p> <blockquote> <p>Mit Objektschnittstellen können Sie Code erstellen, der angibt, welche Methoden eine Klasse implementieren muss, ohne zu definieren, wie diese Methoden implementiert werden. </p> </blockquote> <p>Eine Schnittstelle ist also wie eine Klasse mit vordefinierten Methoden, auf die noch mit der <code>-></code>-Notation zugegriffen werden muss. <p>Die ArrayAccess-Schnittstelle bietet jedoch Zugriff auf Objekte als Arrays. Auf Objekte kann mit <code>$object->property</code> und <code>$object["property"]</code></p> zugegriffen werden. <p>Ich verstehe nicht, wie ArrayAccess es ermöglicht, die Objektsyntax zu ändern. Ich habe einen Code geschrieben, um zu versuchen, den Effekt der <code>ArrayAccess</code>-Methode zu reproduzieren. Es gibt nur eine</em>, aber es wird der Fehler</p> <pre class="brush:php;toolbar:false;">// Verwenden der PHP-ArrayAccess-Schnittstelle Namensraum A { Klasse myclass implementiert ArrayAccess { öffentliche Funktion offsetExists($offset) { return true; öffentliche Funktion offsetGet($offset) { // Verhalten geändert return $this->{$offset} ?? null; } öffentliche Funktion offsetSet($offset, $value) {} öffentliche Funktion offsetUnset($offset) {} } $myclass = new myclass(); $myclass->access = 'Schnittstelle'; echo $myclass['access']; // "Schnittstelle" }; //Ich versuche, meine eigene ArrayAccess-Schnittstelle zu implementieren Namensraum B { Schnittstelle MyArrayAccess { öffentliche Funktion offsetGet($offset); } Klasse myclass implementiert MyArrayAccess { öffentliche Funktion offsetGet($offset) { // Verhalten ändern return $this->{$offset} ?? null; } } $myclass = new myclass(); $myclass->access = 'Schnittstelle'; echo $myclass['access']; // Schwerwiegender Fehler: Nicht erfasster Fehler: Objekt vom Typ Bmyclass kann nicht als Array verwendet werden } </pre> <p>Bitte helfen Sie mir, es richtig zu erklären. Vielen Dank</p>
P粉289775043P粉289775043382 Tage vor531

Antworte allen(1)Ich werde antworten

  • P粉702946921

    P粉7029469212023-09-03 20:17:24

    我并不是说接口“改变了类的行为”,而是说接口使扩展类功能变得容易。

    要理解接口,作为一个面向对象的编程概念,我们首先应该理解它要解决什么问题。

    “Interface”旨在解决什么问题?

    接口是一种契约。这是在 PHP 中实现 duck-typing 的方法。您需要从库编写者的角度思考,他希望向其他人公开功能。例如,

    class Greeter
    {
        public function greet($person)
        {
            echo "Hello, {$person->getName()}!\n";
        }
    }
    

    为了确保库用户知道 $person 需要有 getName() 方法,您可以创建一个类 Person > 有一个 getName() 方法。然后使用 类型声明 来检测代码执行时的潜在错误已解析。

    class Greeter
    {
        public function greet(Person $person)
        {
            echo "Hello, {$person->getName()}!\n";
        }
    }
    
    class Person
    {
        public string $name;
        public function getName(): string
        {
            return $this->name;
        }
    }
    

    假设有另一个库可以用食物来喂养东西:

    class Feeder {
        public function feed(Eater $eater, string $food) {
            $eater->eat($food);
        }
    }
    
    class Animal {
        private $stomach = [];
        public function eat(string $food) {
            $stomach = $food;
        }
    }
    
    

    考虑这个...

    现在,假设用户想要编写一个既可以吃饭又可以打招呼的 Pet 类。用户不想仅仅为了 Pet 再次编写这些功能。

    如何编写 Pet 以便同时使用 GreeterFeeder 库?

    也许是这样的?

    class Pet extends Person, Animal {
    }
    

    不幸的是,PHP 不支持多重继承。一个类只能扩展一个类。上面的代码无效。所以在目前的情况下,用户只能使用其中一个库。

    此外,对于不同的事物,“名称”可能是一个非常不同的概念(例如,一个人可能会使用 getName() 返回 $first_name$last_name )。您的库类中可能没有合理的默认实现 getName() 方法。

    所以,作为一名库编写者,您希望他/她的库对用户尽可能灵活。你能做什么?

    如何用 PHP 中的“接口”解决这个问题?

    接口是方法签名的声明。这是在没有具体类/继承要求的情况下声明库要求的快捷方式。

    使用接口,您可以像这样重写两个库:

    Greeter

    class Greeter {
        public function greet(Namer $namer) {
            echo "Hello, {$namer->getName()}!\n";
        }
    }
    
    interface Namer {
        public function getName(): string;
    }
    

    Feeder

    class Feeder {
        public function feed(Eater $eater, string $food) {
            $eater->eat($food);
        }
    }
    
    interface Eater {
        public function eat(string $food);
    }
    
    

    不需要一个具体的类(或父类继承),一个类可以实现多个接口。所以下面的 Pet 类在 PHP 中是完全有效的:

    class Pet implements Namer, Eater {
        private array $stomach = [];
        private string $name = '';
    
        public function __construct(string $name)
        {
            $this->name = $name;
        }
    
        /**
         * Implements Namer.
         */
        public function getName(): string
        {
            return $this->name;
        }
    
        /**
         * Implements Eater.
         */
        public function eat(string $food)
        {
            $this->stomach[] = $food;
        }
    }
    
    $greeter = new Greeter();
    $feeder = new Feeder();
    $pet = new Pet('Paul');
    
    $greeter->greet($pet);
    $feeder->feed($pet, 'a biscuit');
    

    现在,此 Pet 类的对象可以与 Greeter 库和 Feeder 库一起使用。

    ArrayAccess 接口怎么样?

    ArrayAccess 接口不是由第三方声明的接口库编写者,但由核心 PHP 编写者编写。核心PHP编写器对此提供了更深刻的支持。

    有点像我们之前提到的接口,PHP 为实现它的类提供功能。但核心 PHP 不是提供上面的 GreeterFeeder 示例,而是提供 实现 ArrayAccess 的类的语法糖。这意味着您可以使用更简洁的代码来处理实现 AccessAccess 接口的类。

    在官方示例中,

    <?php
    class Obj implements ArrayAccess {
        private $container = array();
    
        public function __construct() {
            $this->container = array(
                "one"   => 1,
                "two"   => 2,
                "three" => 3,
            );
        }
    
        public function offsetSet($offset, $value) {
            if (is_null($offset)) {
                $this->container[] = $value;
            } else {
                $this->container[$offset] = $value;
            }
        }
    
        public function offsetExists($offset) {
            return isset($this->container[$offset]);
        }
    
        public function offsetUnset($offset) {
            unset($this->container[$offset]);
        }
    
        public function offsetGet($offset) {
            return isset($this->container[$offset]) ? $this->container[$offset] : null;
        }
    }
    

    如果您实现了它们,则代替这些:

    $obj = new Obj;
    $obj->offsetSet(10, "hello");
    $obj->offsetSet(11, "world");
    if ($obj->offsetUnset(12)) {
        $obj->offsetUnset(12);
    }
    echo $obj->offsetGet(11);
    

    您可以将 $obj 与类似数组的语法一起使用,以使代码更短:

    $obj = new Obj;
    
    $obj[10] = "hello";
    $obj[11] = "world";
    if (isset($obj[12])) {
        unset($obj[12]);
    }
    echo $obj[11];
    

    Antwort
    0
  • StornierenAntwort