PHP 接口实际上如何改变类的行为
P粉289775043
2023-09-02 23:49:28
[PHP讨论组]
<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>
我并不是说接口“改变了类的行为”,而是说接口使扩展类功能变得容易。
要理解接口,作为一个面向对象的编程概念,我们首先应该理解它要解决什么问题。
“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以便同时使用Greeter和Feeder库?也许是这样的?
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 不是提供上面的
Greeter或Feeder示例,而是提供 实现 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];