P粉7029469212023-09-03 20:17:24
I'm not saying that interfaces "change the behavior of a class", but that interfaces make extending class functionality easy.
To understand interface, as an object-oriented programming concept, we should first understand what problem it wants to solve.
An interface is a contract. This is how duck-typing is implemented in PHP. You need to think from the perspective of a library writer who wants to expose functionality to others. For example,
class Greeter { public function greet($person) { echo "Hello, {$person->getName()}!\n"; } }
To ensure that library users know that $person
needs to have a getName()
method, you can create a class Person
> that has a getName()
method. Type declarations are then used to detect potential errors when the code is resolved.
class Greeter { public function greet(Person $person) { echo "Hello, {$person->getName()}!\n"; } } class Person { public string $name; public function getName(): string { return $this->name; } }
Suppose there is another library that feeds things with food:
class Feeder { public function feed(Eater $eater, string $food) { $eater->eat($food); } } class Animal { private $stomach = []; public function eat(string $food) { $stomach = $food; } }
Now, suppose the user wants to write a Pet
class that can both eat and greet. Users don't want to have to write these features again just to Pet
them.
How to write Pet
to use both the Greeter
and Feeder
libraries?
Maybe this is the case?
class Pet extends Person, Animal { }
Unfortunately, PHP does not support multiple inheritance . A class can only extend
one class. The above code is not valid. So in the current situation, users can only use one of the libraries.
Additionally, "name" might be a very different concept for different things (e.g., one might use getName() to return
$first_name
and $last_name
代码>). There may not be a reasonable default implementation of the getName()
method in your library class.
So, as a library writer, you want his/her library to be as flexible as possible for the user. what can you do?
An interface is a declaration of method signature. This is a quick way to declare library requirements without concrete class/inheritance requirements.
Using interfaces you can override both libraries like this:
Greeter
Libraryclass Greeter { public function greet(Namer $namer) { echo "Hello, {$namer->getName()}!\n"; } } interface Namer { public function getName(): string; }
Feeder
Libraryclass Feeder { public function feed(Eater $eater, string $food) { $eater->eat($food); } } interface Eater { public function eat(string $food); }
No need for a specific class (or parent class inheritance), a class can implement multiple interfaces. So the following Pet
class is completely valid in 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');
Now objects of this Pet
class can be used with the Greeter
library and the Feeder
library.
ArrayAccess
What about the interface? ArrayAccess The interface is not declared by a third-party interface library writer, but is written by a core PHP writer. The core PHP writer provides deeper support for this.
Somewhat like the interface we mentioned before, PHP provides functionality to the classes that implement it. But instead of providing the Greeter
or Feeder
examples above, core PHP provides syntax sugar for classes that implement ArrayAccess. This means you can use simpler code when working with classes that implement the AccessAccess interface.
In the official example,
<?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; } }
If you implement them, replace these with:
$obj = new Obj; $obj->offsetSet(10, "hello"); $obj->offsetSet(11, "world"); if ($obj->offsetUnset(12)) { $obj->offsetUnset(12); } echo $obj->offsetGet(11);
You can use $obj
with array-like syntax to make the code shorter:
$obj = new Obj; $obj[10] = "hello"; $obj[11] = "world"; if (isset($obj[12])) { unset($obj[12]); } echo $obj[11];