Многократное наследование PHP с интерфейсами

Я пытаюсь понять, как использование интерфейсов дает мне множественное наследование, поскольку я был googling.

class A
{
 function do1(){}
 function do2(){}
 function do3(){}
}

class B extends A
{
 function do4(){}
 function do5(){}
 function do6(){}
}

class C extends B
{
}

В приведенном выше примере класс C имеет все методы из классов A и B. Однако класс B также имеет все методы класса A, которые не нужны.

Мои поиски пришли к использованию интерфейсов для решения этой проблемы, перемещая методы в класс и создавая интерфейсы, как показано ниже.

interface A
{
     function do1();
     function do2();
     function do3();
}

interface B
{
     function do4();
     function do5();
     function do6();
}

class C implements A, B
{
     function do1(){}
     function do2(){}
     function do3(){}
     function do4(){}
     function do5(){}
     function do6(){}
}

Я действительно не вижу, как это решает проблему, потому что весь код находится в новом классе. Если бы я просто хотел использовать класс A как изначально, мне пришлось бы создать новый класс, реализующий интерфейс A и скопировавший тот же код в новый класс.

Есть ли что-то, что мне не хватает?

Ответ 1

PHP не имеет множественного наследования. Однако, если у вас есть PHP 5.4, вы можете использовать traits, чтобы избежать того, чтобы каждый класс копировал код.

interface A {
    public function do1();
    public function do2();
    public function do3();
}

trait Alike {
    public function do1() { }
    public function do2() { }
    public function do3() { }
}


interface B {
    public function do4();
    public function do5();
    public function do6();
}

trait Blike {
    public function do4() { }
    public function do5() { }
    public function do6() { }
}


class C implements A, B {
    use Alike, Blike;
}

class D implements A {
    use Alike;

    // You can even "override" methods defined in a trait
    public function do2() { }
}

Обратите внимание, что вам нужно как реализовать интерфейс, так и использовать его (или, конечно же, предоставить свою собственную реализацию). И C и D вообще не связаны, за исключением как реализации интерфейса A. Черты - это в основном просто копия и вставка на уровне интерпретатора и не влияют на наследование.

Ответ 2

Первое, что нужно знать о интерфейсах, - это то, что они НЕ используются для наследования. Это очень важно понять. Если вы пытаетесь заставить несколько классов использовать один и тот же конкретный код, для этого не нужен интерфейс.

Второе, что нужно понять, это разница между кодом клиента и кодом обслуживания.

Клиентский код по существу является "последним шагом" в последовательности запросов на данные. Контроллер или представление в MVC можно считать кодом клиента. Модель, между тем, может считаться служебным кодом.

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

Итак, чтобы дать вам конкретный пример:

Код клиента - класс ProfileViewController для профиля профиля пользователя

class ProfileViewController
{
    public function showProfile(User $user)
    {
         $user->getProfile();
    }
}

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

class User
{
    public function getProfile()
    {
         $profile = Do some SQL query here or something
         return $profile;
    }
}

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

Теперь есть две проблемы:

  • Вам нужно гарантировать, что все, что вы передадите, будет содержать метод getProfile().
  • showProfile() не удастся, если вы передадите что-либо, кроме объекта User.

1 легко решить с помощью абстрактных классов и методов (или через интерфейсы). 2 сначала звучит легко, потому что вы можете просто сделать Модераторы, Администраторы и Члены всеми подклассами базового класса User.

Но тогда, что происходит, когда по дороге, помимо профилей USER, вы хотите иметь общие профили для вещей. Возможно, вы хотите показать профили спортивных игроков или даже профили знаменитостей. Они не являются пользователями, но у них все еще есть страницы профилей/подробностей.

Поскольку они не являются пользователями, может не иметь смысла рассматривать их подклассы пользователя.

Итак, теперь ты немного застрял. showProfile() должен иметь возможность принимать больше, чем просто объект User. На самом деле, вы не знаете, какой тип объекта вы в конечном итоге захотите пройти там. Но в то же время, поскольку вы всегда хотите захватить $user- > getProfile(), все, что вы передаете, должно быть достаточно общим для передачи, и реализовать конкретный метод getProfile().

Решение? Интерфейсы!!!!!

Сначала некоторый служебный код

// First define an interface for ANY service object that will have a profile

interface IHasProfile
{
    public function getProfile();
}


// Next, define the class for an object that should have a profile. I'll do a bunch for the sake of an example...

class User implements IHasProfile
{
    public function getProfile()
    {
         $profile = Your unique user profile query here
         return $profile;
    }
}


class Celebrity implements IHasProfile
{
    public function getProfile()
    {
         $profile = Your unique celebrity profile query here
         return $profile;
    }
}


class Car implements IHasProfile
{
    public function getProfile()
    {
         $profile = Your unique vehicle profile query goes here
         return $profile;
    }
}

Далее, клиентский код, который будет использовать его

class ProfileViewController
{
    public function showProfile(IHasProfile $obj)
    {
         $obj->getProfile();
    }
}

И у вас это есть. showProfile() теперь полностью абстрагирован, что ему все равно, какой объект он получает, он только заботится о том, чтобы объект имел общедоступный метод getProfile(). Итак, теперь вы можете создавать новые типы объектов для вашего сердечного контента, и если они предназначены для создания профилей, вы можете просто дать им "реализует IHasProfile", и они автоматически будут работать с showProfile().

Вид надуманного примера, но он должен проиллюстрировать, по крайней мере, концепцию интерфейсов.

Конечно, вы могли бы просто "лениться" и вообще не выводить объект вообще и, таким образом, позволять ЛЮБОЙ объект быть переданным. Но это отдельная тема целиком;)

Ответ 3

Множественное наследование возможно только для интерфейсов!

например, мой вывод для него:

php > interface A{};
php > interface B{};
php > interface C extends A,B{};
php > class D implements C{};
php > $d = new D();
php > echo ($d instanceof A);
1
  • Я создал интерфейсы A и B и интерфейс C расширяет их.
  • После того, как у нас есть класс D, который реализует интерфейс C
  • Наконец, я спрашиваю, является ли объект $d экземпляром интерфейса A yeah it true

Для lulz я пытаюсь создать класс E, который расширяет классы D и stdclass и получает ошибку!

php > class E extends D, stdclass{};
PHP Parse error:  syntax error, unexpected ',', expecting '{' in php shell code on line 1

Parse error: syntax error, unexpected ',', expecting '{' in php shell code on line 1

Ответ 4

Множественное наследование невозможно в PHP, как во многих поддерживаемых языках OOP

Смотрите аналогичную тему здесь. Тема находится в AS3, но дает вам ответ.

Чтобы ответить, в частности, на решение с использованием интерфейсов, ответили в том же сообщении здесь

Надеюсь, что это поможет

Ответ 5

Как сказано здесь от @tonicospinelli, кажется, что в деле, PHP допускает взаимное наследование наследования, но не ясно объяснено, просто приведенный пример

Способ множественного наследования, хотя внутри PHP проходит, используя Traits, что реализует интерфейсы.

Как только вы объявите класс, реализующий "мультиинтерфейс" (1), вы можете использовать уже определенные черты, чтобы гарантировать, что наследование хорошо выполнено.

(1). Говоря "multi-interface", я имею в виду класс, реализующий интерфейс, который простирается от нескольких других интерфейсов.