PHP 接口实际上如何改变类的行为
P粉289775043
P粉289775043 2023-09-02 23:49:28
0
1
603
<p>根据 PHP 文档,</p> <blockquote> <p>对象接口允许您创建指定类必须实现哪些方法的代码,而无需定义这些方法的实现方式。</p> </blockquote> <p>因此,接口就像一个具有预定义方法的类,仍然需要使用 <code>-></code> 符号来访问</p> <p>但是,ArrayAccess 接口提供对对象作为数组的访问。可以使用 <code>$object->property</code> 和 <code>$object["property"]</code></p> 访问对象 <p>我无法理解 ArrayAccess 如何使更改对象语法成为可能。我编写了一段代码来尝试复制 <code>ArrayAccess</code> 方法<em>仅一个</em>的效果,但它抛出错误</p> <pre class="brush:php;toolbar:false;">// Using the PHP ArrayAccess Interface namespace A { class myclass implements \ArrayAccess { public function offsetExists($offset) { return true; } public function offsetGet($offset) { // changed behaviour return $this->{$offset} ?? null; } public function offsetSet($offset, $value) {} public function offsetUnset($offset) {} } $myclass = new myclass(); $myclass->access = 'Interface'; echo $myclass['access']; // "Interface" }; //Trying to implement my own ArrayAccess Interface namespace B { interface MyArrayAccess { public function offsetGet($offset); } class myclass implements MyArrayAccess { public function offsetGet($offset) { // change behaviour return $this->{$offset} ?? null; } } $myclass = new myclass(); $myclass->access = 'Interface'; echo $myclass['access']; // Fatal error: Uncaught Error: Cannot use object of type B\myclass as array } </pre> <p>请帮我正确解释一下。谢谢</p>
P粉289775043
P粉289775043

全部回复(1)
P粉702946921

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

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

“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];
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板