mirror of
https://github.com/php/doc-ru.git
synced 2026-03-26 16:52:12 +01:00
374 lines
13 KiB
XML
374 lines
13 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<!-- EN-Revision: 3d522c890d98c563bb283cf89ec5da5f535cfb8f Maintainer: shein Status: ready -->
|
||
<!-- Reviewed: no -->
|
||
<sect1 xml:id="language.oop5.interfaces" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||
<title>Интерфейсы объектов</title>
|
||
<para>
|
||
Интерфейсы объектов позволяют создавать код, который
|
||
указывает, какие методы должен реализовать класс, без
|
||
необходимости определять, как именно они должны быть реализованы.
|
||
Интерфейсы разделяют пространство имён с классами и трейтами,
|
||
поэтому они не могут называться одинаково.
|
||
</para>
|
||
<para>
|
||
Интерфейсы объявляются так же, как и обычные классы, но с использованием
|
||
ключевого слова <literal>interface</literal> вместо <literal>class</literal>.
|
||
Тела методов интерфейсов должны быть пустыми.
|
||
</para>
|
||
<para>
|
||
Все методы, определённые в интерфейсах, должны быть общедоступными, что
|
||
следует из самой природы интерфейса.
|
||
</para>
|
||
<para>
|
||
На практике интерфейсы используются в двух взаимодополняющих случаях:
|
||
</para>
|
||
<simplelist>
|
||
<member>
|
||
Чтобы позволить разработчикам создавать объекты разных классов, которые могут использоваться взаимозаменяемо,
|
||
поскольку они реализуют один и тот же интерфейс или интерфейсы.
|
||
Типичный пример - несколько служб доступа к базе данных, несколько платёжных шлюзов или разных стратегий кеширования.
|
||
Различные реализации могут быть заменены без каких-либо изменений в коде, который их использует.
|
||
</member>
|
||
<member>
|
||
Чтобы разрешить функции или методу принимать и оперировать параметром, который соответствует
|
||
интерфейсу, не заботясь о том, что ещё может делать объект или как он реализован.
|
||
Эти интерфейсы часто называют <literal>Iterable</literal>, <literal>Cacheable</literal>, <literal>Renderable</literal>
|
||
и так далее, чтобы описать их поведение.
|
||
</member>
|
||
</simplelist>
|
||
<para>
|
||
Интерфейсы могут определять
|
||
<link linkend="language.oop5.magic">магические методы</link>, требуя от реализующих классов реализации этих методов.
|
||
</para>
|
||
|
||
<note>
|
||
<para>
|
||
Хотя они поддерживаются, использование <link linkend="language.oop5.decon.constructor">конструкторов</link>
|
||
в интерфейсах настоятельно не рекомендуется. Это значительно снижает гибкость объекта, реализующего интерфейс.
|
||
Кроме того, к конструкторам не применяются правила наследования, что может привести к противоречивому
|
||
и неожиданному поведению.
|
||
</para>
|
||
</note>
|
||
|
||
<sect2 xml:id="language.oop5.interfaces.implements">
|
||
<title><literal>implements</literal></title>
|
||
<para>
|
||
Для реализации интерфейса используется оператор <literal>implements</literal>.
|
||
Класс должен реализовать все методы, описанные в интерфейсе, иначе
|
||
произойдёт фатальная ошибка. При желании классы могут реализовывать
|
||
более одного интерфейса, разделяя каждый интерфейс запятой.
|
||
</para>
|
||
<warning>
|
||
<para>
|
||
Класс, реализующий интерфейс, может использовать для своих параметров имя, отличное
|
||
от имени интерфейса. Однако, начиная с PHP 8.0, в языке поддерживаются
|
||
<link linkend="functions.named-arguments">именованные аргументы</link>, и вызывающий код может полагаться на имя параметра в интерфейсе.
|
||
По этой причине настоятельно рекомендуется, чтобы разработчики использовали те же имена параметров, что и реализуемый интерфейс.
|
||
</para>
|
||
</warning>
|
||
<note>
|
||
<para>
|
||
Интерфейсы могут быть унаследованы друг от друга, так же, как и классы,
|
||
с помощью оператора <link linkend="language.oop5.inheritance">extends</link>.
|
||
</para>
|
||
</note>
|
||
<note>
|
||
<para>
|
||
Класс, реализующий интерфейс, должен объявить все методы в интерфейсе с
|
||
<link linkend="language.oop.lsp">совместимой сигнатурой</link>. Класс может
|
||
реализовывать несколько интерфейсов, которые объявляют метод с одинаковым именем.
|
||
В этом случае реализация должна следовать <link linkend="language.oop.lsp">правилам совместимости сигнатуры</link>
|
||
для всех интерфейсов. Таким образом, можно применять <link linkend="language.oop5.variance">ковариантность и контравариантность</link>.
|
||
</para>
|
||
</note>
|
||
</sect2>
|
||
<!-- Move this to OOP constants page? -->
|
||
<sect2 xml:id="language.oop5.interfaces.constants">
|
||
<title>Константы</title>
|
||
<para>
|
||
Интерфейсы могут содержать константы. Константы интерфейсов
|
||
работают точно так же, как и <link linkend="language.oop5.constants">константы классов</link>.
|
||
До PHP 8.1.0 они не могли быть переопределены классом или интерфейсом, который их наследует.
|
||
</para>
|
||
</sect2>
|
||
<sect2 xml:id="language.oop5.interfaces.examples">
|
||
&reftitle.examples;
|
||
<example xml:id="language.oop5.interfaces.examples.ex1">
|
||
<title>Пример интерфейса</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
// Объявим интерфейс 'Template'
|
||
interface Template
|
||
{
|
||
public function setVariable($name, $var);
|
||
public function getHtml($template);
|
||
}
|
||
|
||
// Реализация интерфейса
|
||
// Это будет работать
|
||
class WorkingTemplate implements Template
|
||
{
|
||
private $vars = [];
|
||
|
||
public function setVariable($name, $var)
|
||
{
|
||
$this->vars[$name] = $var;
|
||
}
|
||
|
||
public function getHtml($template)
|
||
{
|
||
foreach($this->vars as $name => $value) {
|
||
$template = str_replace('{' . $name . '}', $value, $template);
|
||
}
|
||
|
||
return $template;
|
||
}
|
||
}
|
||
|
||
// Это не будет работать
|
||
// Fatal error: Class BadTemplate contains 1 abstract methods
|
||
// and must therefore be declared abstract (Template::getHtml)
|
||
// (Фатальная ошибка: Класс BadTemplate содержит 1 абстрактный метод
|
||
// и поэтому должен быть объявлен абстрактным (Template::getHtml))
|
||
class BadTemplate implements Template
|
||
{
|
||
private $vars = [];
|
||
|
||
public function setVariable($name, $var)
|
||
{
|
||
$this->vars[$name] = $var;
|
||
}
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex2">
|
||
<title>Наследование интерфейсов</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
interface A
|
||
{
|
||
public function foo();
|
||
}
|
||
|
||
interface B extends A
|
||
{
|
||
public function baz(Baz $baz);
|
||
}
|
||
|
||
// Это сработает
|
||
class C implements B
|
||
{
|
||
public function foo()
|
||
{
|
||
}
|
||
|
||
public function baz(Baz $baz)
|
||
{
|
||
}
|
||
}
|
||
|
||
// Это не сработает и выдаст фатальную ошибку
|
||
class D implements B
|
||
{
|
||
public function foo()
|
||
{
|
||
}
|
||
|
||
public function baz(Foo $foo)
|
||
{
|
||
}
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.variance.multiple.interfaces">
|
||
<title>Совместимость с несколькими интерфейсами</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
class Foo {}
|
||
class Bar extends Foo {}
|
||
|
||
interface A {
|
||
public function myfunc(Foo $arg): Foo;
|
||
}
|
||
|
||
interface B {
|
||
public function myfunc(Bar $arg): Bar;
|
||
}
|
||
|
||
class MyClass implements A, B
|
||
{
|
||
public function myfunc(Foo $arg): Bar
|
||
{
|
||
return new Bar();
|
||
}
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex3">
|
||
<title>Множественное наследование интерфейсов</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
interface A
|
||
{
|
||
public function foo();
|
||
}
|
||
|
||
interface B
|
||
{
|
||
public function bar();
|
||
}
|
||
|
||
interface C extends A, B
|
||
{
|
||
public function baz();
|
||
}
|
||
|
||
class D implements C
|
||
{
|
||
public function foo()
|
||
{
|
||
}
|
||
|
||
public function bar()
|
||
{
|
||
}
|
||
|
||
public function baz()
|
||
{
|
||
}
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex4">
|
||
<title>Интерфейсы с константами</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
interface A
|
||
{
|
||
const B = 'Константа интерфейса';
|
||
}
|
||
|
||
// Выведет: Константа интерфейса
|
||
echo A::B;
|
||
|
||
|
||
class B implements A
|
||
{
|
||
const B = 'Константа класса';
|
||
}
|
||
|
||
// Выведет: Константа класса
|
||
// До PHP 8.1.0 этот код не будет работать,
|
||
// потому что было нельзя переопределять константы.
|
||
echo B::B;
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex5">
|
||
<title>Интерфейсы с абстрактными классами</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
interface A
|
||
{
|
||
public function foo(string $s): string;
|
||
|
||
public function bar(int $i): int;
|
||
}
|
||
|
||
// Абстрактный класс может реализовывать только часть интерфейса.
|
||
// Классы, расширяющие абстрактный класс, должны реализовать все остальные.
|
||
abstract class B implements A
|
||
{
|
||
public function foo(string $s): string
|
||
{
|
||
return $s . PHP_EOL;
|
||
}
|
||
}
|
||
|
||
class C extends B
|
||
{
|
||
public function bar(int $i): int
|
||
{
|
||
return $i * 2;
|
||
}
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example xml:id="language.oop5.interfaces.examples.ex6">
|
||
<title>Одновременное расширение и внедрение</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
class One
|
||
{
|
||
/* ... */
|
||
}
|
||
|
||
interface Usable
|
||
{
|
||
/* ... */
|
||
}
|
||
|
||
interface Updatable
|
||
{
|
||
/* ... */
|
||
}
|
||
|
||
// Порядок ключевых слов здесь важен. "extends" должно быть первым.
|
||
class Two extends One implements Usable, Updatable
|
||
{
|
||
/* ... */
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<para>
|
||
Интерфейс, совместно с объявлениями типов, предоставляет отличный
|
||
способ проверки того, что определённый объект содержит определённый
|
||
набор методов. Смотрите также оператор
|
||
<link linkend="language.operators.type">instanceof</link> и
|
||
<link linkend="language.types.declarations">объявление типов</link>.
|
||
</para>
|
||
</sect2>
|
||
|
||
</sect1><!-- Keep this comment at the end of the file
|
||
Local variables:
|
||
mode: sgml
|
||
sgml-omittag:t
|
||
sgml-shorttag:t
|
||
sgml-minimize-attributes:nil
|
||
sgml-always-quote-attributes:t
|
||
sgml-indent-step:1
|
||
sgml-indent-data:t
|
||
indent-tabs-mode:nil
|
||
sgml-parent-document:nil
|
||
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
||
sgml-exposed-tags:nil
|
||
sgml-local-catalogs:nil
|
||
sgml-local-ecat-files:nil
|
||
End:
|
||
vim600: syn=xml fen fdm=syntax fdl=2 si
|
||
vim: et tw=78 syn=sgml
|
||
vi: ts=1 sw=1
|
||
-->
|