What is an abstract class and interface in php

PHP: Interface and Abstract Class

One of the most popular questions on the interview is “What is the difference in interfaces and abstract classes?”. So let’s see the difference.

Interfaces

Before getting into theory, let’s refresh in memory how an interface is defined:

 interface InterfaceName  public function method($parameter); >

An interface can contain methods and constants, but can’t contain any variables. All methods must be public and have no implementation.

Inheritance

In PHP one interface can be inherited from another by extends keyword:

 interface ParentInterface  public function method($parameter); > interface ChildInterface extends ParentInterface  public function another_method($parameter); >

One difference with classes comes with the multiple inheritance, which is available for interfaces:

 interface LoggerInterface extends WritableInterface, ReadableInterface  // . methods >

Implementation

When we create an interface we declare that methods defined in it will be available in any class that implements the interface. For example, when we need to guarantee that an object accepts an array of data, we go and create an interface:

 interface ArrayDataTransformer  public function getTransformedData(array $data = []); > class CsvTransformer implements ArrayDataTransformer  public function getTransformedData(array $data = [])  // implementation > >

And then every class that implements this interface must have implementations for the interface methods. And the rest of our code base now knows that CsvTransformer class objects have getTransformedData method.

But a class can’t implement two interfaces that share the same function names because they have no bodies and it would cause ambiguity.

Abstract Class

An abstract class is also an interface. But the key difference here is that an abstract class provides the implementation logic. Let’s rewrite CsvTransformer class with the use of abstract class:

 abstract class ArrayDataTransformer  private $data; public function setData($data)  $this->data = $data; > abstract public function getTransformedData($data = []); >

Now with the abstract class, we have declared a new data type in our language, called ArrayDataTransformer. This data type provides the interface of two public methods. The first one is a setter. The second will be specialized in child classes:

 class CsvTransformer extends ArrayDataTransformer  public function getTransformedData($data = [])  // child implementation > >

True Constants

When using abstract classes our constants may have variable values:

 abstract class AbstractTable  const TABLE = null; > class UsersTable extends AbstractTable  const TABLE = 'users'; > class OrdersTable extends AbstractTable  const TABLE = 'orders'; >

We can override and redeclare constants of a parent class in its child classes. Sometimes it may be useful, like in the previous example of an application’s persistence layer.

But if we need the real unchangeable constants, we can write them into interfaces. For example PI constant, then can’t be redeclared:

 interface MathInterface  const PI = 3.14159; > class MathOperations implements MathInterface <> var_dump(MathOperations::PI); // 3.14159

Summary

With the abstract class with define a new data type in our language, which has its own interface. In child classes, we may override this interface or implement new specializations. Abstract classes may be useful when we need to declare a new data type with its own logic implementation.

The interface provides only a public declaration of the methods. It is a guarantee for the client code that will interact with the objects that implement this interface. The client code doesn’t care about an object’s class, it’s private or protected methods. We may use interfaces when we don’t care about the concrete logic implementation and our client code only needs a special functionality, for example in dependency injection.

Источник

PHP: Интерфейсы vs Абстрактные классы

Недавно я опубликовал статью об улучшении PHP кода с помощью интерфейсов. Она охватывает основы того что такое интерфейс, что он может делать. И как вы можете использовать его сделав свой PHP код более расширяемым и поддерживаемым. Один из вопросов, заданных в комментариях к статье был от разработчиков, которые хотели знать «когда я должен использовать интерфейс вместо абстрактного класса?». Я подумал и решил написать статью, что бы объяснить различия между абстрактными классами и интерфейсами в PHP и дать краткий обзор того, когда вы должны использовать каждый из них.

Что такое Интерфейсы

В общих чертах, Интерфейс должен описывать, как будет построен класс реализующий его, это похоже на план, описывающий публичные методы и константы, которые должны быть реализованы.

Интерфейсы должны:

  • Использоваться для определения публичных методов класса.
  • Использоваться для определения констант класса.

Интерфейсы не должны:

  • Использоваться сами по себе.
  • Использоваться для определения приватных или защищённых методов класса.
  • Использоваться для определения свойств класса.

Интерфейсы используются для определения публичных методов, которые должен включать класс. Важно помнить, что интерфейс всегда предназначен для реализации классом, поэтому в нём вы определяете только сигнатуру метода, например:

interface HomeInterface 

const MATERIAL = 'Brick';

public function openDoor(): void;

public function getRooms(): array;

public function hasGarden(): bool;
>

а не как в следующем примере:

interface HomeInterface 

public string $material = 'Brick';

public function openDoor(): void

// Открываем дверь.
>

public function getRooms(): array

// Даём информацию о комнатах.
>

public function hasGarden(): bool

// Задаём есть ли у дома сад.
>
>

Согласно php.net, интерфейсы служат нескольким целям:

  1. Позволить разработчикам создавать объекты разных классов, которые могут использоваться взаимозаменяемо, поскольку они реализуют один и тот же интерфейс или интерфейсы. Типичный пример — несколько служб доступа к базе данных, несколько платёжных шлюзов или разные стратегии кэширования. Различные реализации могут быть заменены, не требуя каких-либо изменений в коде, который их использует.
  2. Позволить функции или методу принимать и оперировать параметром, который соответствует интерфейсу, не заботясь о том, что ещё может делать объект или как он реализован. Эти интерфейсы часто называют Iterable , Cacheable , Renderable и т.д., чтобы описать поведение.

Используя наш выше описанный интерфейс и придерживаясь аналогии с домом, мы могли бы создавать различные классы реализующие HomeInterface такие, как House , Flat или Caravan . Используя интерфейс, мы можем быть уверены, что наш класс содержит три необходимых метода и все используют правильную сигнатуру метода. Например, у нас может быть класс House , который выглядит так:

class House implements HomeInterface 

public function openDoor(): void

// Открываем дверь здесь.
>

public function getRooms(): array

// Даём информацию о комнатах.
>

public function hasGarden(): bool

// Задаём есть ли у дома сад.
>
>

Что такое Абстрактные классы

Абстрактные классы PHP очень похожи на интерфейсы PHP; они не предназначены быть классами сами по себе и представляют базовые методы без реализации.

Возьмём пример с домами, приведённый выше, если интерфейс является вашим планом, то абстрактный класс — это ваша модель выставочного зала. Он работает, и это отличный пример дома, но вам всё равно нужно его обставить и украсить, чтобы сделать его своим.

Абстрактный класс может:

  • Использоваться для определения сигнатур методов класса с использованием abstract методов (аналогично интерфейсам).
  • Использоваться для определения методов.
  • Использоваться для определения констант класса.
  • Использоваться для определения свойств класса.
  • Расширятся дочерним классом.

Абстрактный класс не может:

Что бы понять, что это означает, рассмотрим пример абстрактного класса:

abstract class House 

const MATERIAL = 'Brick';

abstract public function openDoor(): void;

public function getRooms(): array

return [
'Bedroom',
'Bathroom',
'Living Room',
'Kitchen',
];
>

public function hasGarden(): bool

return true;
>
>

Наш класс House объявлен abstract — это означает, что мы не можем использовать его напрямую. Для использования нужно его унаследовать. Например, давайте создадим класс MyHouse , который расширяет абстрактный класс House :

class MyHouse extends House 

public function openDoor(): void

// Открываем дверь.
>

public function getRooms(): array

return [
'Bedroom One',
'Bedroom Two',
'Bathroom',
'Living Room',
'Kitchen',
];
>
>
// Это не будет работать: 
$house = new House();

// Это будет работать:
$house = new MyHouse();

Вы могли заметить, что в классе House мы объявили абстрактный публичный метод openDoor() . Это позволяет нам определить сигнатуру метода, которую дочерний класс должен включать, аналогично тому, как мы делали бы с интерфейсом. Это действительно удобно, если вы хотите поделиться функциональностью с дочерними классами, но при этом обеспечить возможность собственной реализации некоторых методов.

В этом случае дочерний класс мог бы переопределить методы getRooms() и hasGarden() , но не должен был бы их включать. Что продемонстрировать это, мы переопределили метод getRooms() показав, как мы можем изменить его поведение в дочернем классе.

Как решить, что использовать

Это зависит от вашей цели. Сохранив аналогию с домом, если вы создаёте чертежи, которые в дальнейшем можно использовать для проектирования домов разных типов, вам нужен интерфейс.

Если вы построили дом и вам нужно создать копии с улучшениями, то вам нужен абстрактный класс.

Приведу несколько примеров:

Когда использовать Интерфейс

Чтобы помочь понять, когда нужно использовать интерфейс, давайте рассмотрим пример. Допустим, у нас ест класс ConstructionCompany включающий метод buildHome() , который выглядит следующим образом:

class ConstructionCompany 

public function buildHome($home)

// Строим дом.

return $home;
>
>

Теперь предположим, что у нас есть 3 разных класса, которые мо хотим создать и передать методу buildHome() :

  1. class MyHouse implements HomeInterface extends House
  2. class MyCaravan implements HomeInterface
  3. class MyFlat implements HomeInterface

Как мы видим, класс MyHouse расширяет абстрактный класс House ; и это имеет смысл к концептуальной точки зрения, потому что дом есть дом. Однако класс MyCaravan или MyFlat не имеет смысла расширять от абстрактного класса, потому что ни один из них не является домом.

Итак, поскольку наша строительная компания может строить дома, фургоны и квартиры, это правило исключает указание параметра $home в методе buildHome() как экземпляра House .

Однако это было бы идеальным местом для указания типа нашему методу, что бы разрешить принимать только классы реализующие HomeInterface . В качестве примера мы могли бы обновить метод следующим образом:

class ConstructionCompany 

public function buildHome(HomeInterface $home)

// Строим дом.

return $home;
>
>

В результате мы можем быть уверенными, передавая значение дом, фургон или квартира, что наш класс ConstructionCompany получит необходимую информацию. Переданный объект home всегда будет содержать необходимые нам методы.

Вы могли подумать: Почему бы нам просто не создать абстрактный класс Home вместо интерфейса? . Однако важно помнить, что PHP поддерживает только одиночное наследование и что класс не может расширять более одного родителя. Таким образом, это будет довольно сложно, если вы захотите расширить один из своих классов в будущем.

Когда использовать Абстрактный класс

Возьмём сценарий аналогичный приведённому выше. Давайте представим, что у нас есть HouseConstructionCompany , похожая на нашу ConstructionCompany . Но в этом примере предположим, что компания HouseConstructionCompany строит только дома и ничего больше.

Поскольку мы знаем, что нам нужно уметь строить только дома, мы можем указать, что бы наш метод принимал только те классы, которые являются расширением абстрактного класса Home . Это может быть действительно полезно, так как мы всегда будем уверены, что передаём типы только тех домов которые строительная компания строит. Например:

class HouseConstructionCompany 

public function buildHouse(House $house)

// Build the house here.

return $house;
>
>

Вывод

Надеюсь эта статья даст представление о различиях между интерфейсами и абстрактными классами в PHP. А так же различных сценариев того, когда следует их использовать.

Эти три структуры могут сбить с толку новичков в PHP и опытных разработчиков, поэтому давайте рассмотрим, что делает каждая из них и когда их лучше использовать

Важно убедится, что ваш код легко читается, поддерживается, расширяется и тестируется. Один из способов улучшить эти факторы — использовать интерфейсы.

Источник

Читайте также:  Концепция языка программирования python
Оцените статью