1
0
mirror of https://github.com/php/doc-ru.git synced 2026-03-24 07:42:22 +01:00
Files
archived-doc-ru/language/oop5/interfaces.xml
Mikhail Alferov 60caf19df7 Update 565bd8b to En (#1087)
* Update interfaces.xml to en

* Update construct.xml to en

* Update construct.xml

* Update construct.xml to en
2025-07-10 04:07:31 +03:00

479 lines
18 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: 565bd8b6cf2cae44ae2bc54ef6dbe6ee70ddfefd 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>
для каждого интерфейса, когда класс реализует больше одного интерфейса,
в котором объявили методы с одинаковым названием. Поэтому при организации иерархии типов PHP-разработчики
пользуются доступной в языке <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.properties">
<title>Properties</title>
<simpara>
Начиная с PHP 8.4.0 в интерфейсах разрешили объявлять свойства.
При объявлении свойств потребуется указать, доступно ли свойство для чтения,
записи или и того, и другого.
Объявление интерфейса применяется только к открытому доступу на чтение и запись.
</simpara>
<simpara>
Класс удовлетворяет свойству интерфейса несколькими способами:
класс определяет открытое свойство,
<link linkend="language.oop5.property-hooks.virtual">виртуальное свойство</link>,
которое реализует только тот хук, который соответствует хуку интерфейса,
или определяет <literal>readonly</literal>-свойство, которое удовлетворяет свойству интерфейса для чтения.
Однако в классе нельзя ограничивать доступ на запись свойства модификатором <literal>readonly</literal>,
если в интерфейсе свойство объявили доступным для записи.
</simpara>
<example>
<title>Пример свойств интерфейса</title>
<programlisting role="php">
<![CDATA[
<?php
interface I
{
// Класс, в котором реализуется свойство, ДОЛЖЕН объявить открытое для чтения свойство,
// но объявление свойства в интерфейсе не ограничивает объявление доступа на запись свойства в классе
public string $readable {
get;
}
// Класс, в котором реализуется свойство, должен объявить открытое для записи свойство,
// но объявление свойства в интерфейсе не ограничивает объявление доступа на чтение свойства в классе
public string $writeable {
set;
}
// Класс, в котором реализуется свойство, должен объявить свойство,
// открытое как для чтения, так и для записи
public string $both {
get;
set;
}
}
// Класс реализует каждое из трёх свойств традиционно, без хуков.
// Такая реализация свойств допустима
class C1 implements I
{
public string $readable;
public string $writeable;
public string $both;
}
// Класс реализует каждое из трёх свойств и определяет только те хуки,
// которые потребовал интерфейс. Такая реализация свойств тоже допустима
class C2 implements I
{
private string $written = '';
private string $all = '';
// Класс реализует только хук для чтения, чтобы создать виртуальное свойство.
// Такое определение удовлетворяет требованию «публичной открытости для чтения».
// Свойство недоступно для записи, но интерфейс и не требует открытого доступа для записи
public string $readable {
get => strtoupper($this->writeable);
}
// Интерфейс требует только того, чтобы класс определил свойство, открытое для записи,
// но включение хука для операции чтения тоже допустимо.
// Пример создаёт виртуальное свойство, и это нормально
public string $writeable {
get => $this->written;
set {
$this->written = $value;
}
}
// Свойство требует как операции чтения, так и операции записи,
// поэтому потребуется либо реализовать оба хука, либо разрешить операциям чтения и записи
// поведение по умолчанию
public string $both {
get => $this->all;
set {
$this->all = strtoupper($value);
}
}
}
?>
]]>
</programlisting>
</example>
</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
-->