Web

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

Если у меня есть несколько классов с функциями, которые мне нужны, но я хочу хранить их отдельно для организации, могу ли я расширить класс, чтобы иметь их оба?

То есть:

class a extends b extends c

Я знаю, как расширять классы по одному, но я ищу метод мгновенного расширения класса, используя несколько базовых классов — AFAIK, наверняка должны быть способы обойти это, не прибегая к class c extends b, class b extends a.

 

Ответ 1

Если вы действительно хотите подделать множественное наследование в PHP 5.3, вы можете использовать магическую функцию __call(). Это не совсем корректно, хотя и работает с точки зрения пользователя класса A:

class B {

    public function method_from_b($s) {

        echo $s;

    }

}

 

class C {

    public function method_from_c($s) {

        echo $s;

    }

}

 

class A extends B {

  private $c;

  public function __construct() {

    $this->c = new C;

  }

  // подделка "расширяет класс C" с помощью магической функции

  public function __call($method, $args) {

    $this->c->$method($args[0]);

  }

}

 

 

$a = new A;

$a->method_from_b("abc");

$a->method_from_c("def");

 

Ответ 2

Вы можете использовать трейты, которые стали доступны в PHP 5.4.

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

Они признаны за их потенциал в поддержке лучшей композиции и повторного использования, отсюда их интеграция в новые версии языков, таких как Perl 6, Squeak, Scala, Slate и Fortress. Трейты также были перенесены на Java и C#. Дополнительная информация: https://wiki.php.net/rfc/traits.

 

Ответ 3

Классы не должны быть просто коллекциями методов. Класс должен представлять абстрактную концепцию, имеющую как состояние (поля), так и поведение (методы), которое изменяет состояние. Использование наследования только для получения желаемого поведения звучит как плохой ОО дизайн, и именно по этой причине многие языки запрещают множественное наследование: чтобы предотвратить «спагетти-наследование», т. е. расширение 3 классов, потому что каждый из них имеет нужный вам метод, и в итоге получается класс, который наследует 100 методов и 20 полей, но использует только 5 из них.

 

Ответ 4

Я полагаю, что в скором времени планируется добавление «миксов».

Но до тех пор придерживайтесь общепринятого решения. Вы можете немного абстрагироваться от этого, чтобы сделать «расширяемый» класс:

class Extendable{

  private $extender=array();

  public function addExtender(Extender $obj){

    $this->extenders[] = $obj;

    $obj->setExtendee($this);

  }

  public function __call($name, $params){

    foreach($this->extenders as $extender){

       //do reflection to see if extender has this method with this argument count

       if (method_exists($extender, $name)){

          return call_user_func_array(array($extender, $name), $params);

       }

    }

  }

}

$foo = new Extendable();

$foo->addExtender(new OtherClass());

$foo->other_class_method();

Обратите внимание, что в этой модели «OtherClass» получает «знание» о $foo. OtherClass должен иметь публичную функцию под названием «setExtendee», чтобы установить эту связь. Затем, если его методы вызываются из $foo, он может получить внутренний доступ к $foo. Однако он не получит доступ к каким-либо приватным/защищенным методам/переменным, как это сделал бы настоящий расширенный класс.

 

Ответ 5

Используйте трейты в качестве базовых классов. Затем используйте их в родительском классе. Расширьте его.

trait business{

  function sell(){

 

  }

 

  function buy(){

 

  }

 

  function collectMoney(){

  }

 

}

 

trait human{

 

   function think(){

 

   }

 

   function speak(){

 

   }

 

}

 

class BusinessPerson{

  use business;

  use human;

  // If you have more traits bring more

}

 

 

class BusinessWoman extends BusinessPerson{

 

   function getPregnant(){

 

   }

 

}

 

 

$bw = new BusinessWoman();

$bw ->speak();

$bw->getPregnant();



Ответ 6

<?php

// что если мы хотим расширить более одного класса?

abstract class ExtensionBridge {

    // массив, содержащий все расширенные классы

    private $_exts = array();

    public $_this;

 

    function __construct() {$_this = $this;}

 

    public function addExt($object) {

        $this->_exts[]=$object;

    }

 

    public function __get($varname) {

        foreach($this->_exts as $ext)

        {

            if(property_exists($ext,$varname))

            return $ext->$varname;

        }

    }

 

    public function __call($method,$args) {

        foreach($this->_exts as $ext)

        {

            if(method_exists($ext,$method))

            return call_user_method_array($method,$ext,$args);

        }

        throw new Exception("Этот метод {$method} не существует");

    }

}

 

class Ext1 {

    private $name="";

    private $id="";

    public function setID($id){$this->id = $id;}

    public function setName($name){$this->name = $name;}

    public function getID(){return $this->id;}

    public function getName(){return $this->name;}

}

 

class Ext2 {

    private $address="";

    private $country="";

    public function setAddress($address){$this->address = $address;}

    public function setCountry($country){$this->country = $country;}

    public function getAddress(){return $this->address;}

    public function getCountry(){return $this->country;}

}

 

class Extender extends ExtensionBridge {

    function __construct() {

        parent::addExt(new Ext1());

        parent::addExt(new Ext2());

    }

    public function __toString() {

        return $this->getName().', from: '.$this->getCountry();

    }

}

 

$o = new Extender();

$o->setName("Mahdi");

$o->setCountry("Al-Ahwaz");

echo $o;

?>

 

Ответ 7

PHP 5.4+ и 7+

Начиная с PHP 5.4.0, появились «трейты» — вы можете использовать несколько трейтов в одном классе, так что окончательное решение принимается в зависимости от того, хотите ли вы действительно наследовать или вам просто нужна какая-то «особенность» (трейты). Трейт это, как бы это сказать, уже реализованный интерфейс, который предназначен для простого использования.

Но на самом деле это не множественное наследование, а дочерний экземпляр класса, определенный вне области видимости, также есть сокращение '__call()'  рассмотрите возможность использования просто '$this->childInstance->method(args)' везде, где вам нужен метод класса ExternalClass в «расширенном» классе.

Точный ответ

Как сказано в руководстве по использованию ключевого слова extends:

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

Реальный ответ

Однако это НЕ запрещает вам использовать множественное иерархическое наследование.

Вы можете расширять один класс другим, другой третьим и так далее...

 Так что довольно простым примером может быть:

class firstInheritance{}

class secondInheritance extends firstInheritance{}

class someFinalClass extends secondInheritance{}

//...и так далее...

Важное замечание

Как вы могли заметить, вы можете сделать множественную(2+) целостность по иерархии, только если у вас есть контроль над всеми классами, включенными в процесс, — это означает, что вы не можете применить это решение, например, со встроенными классами или с классами, которые вы просто не можете редактировать; если вы хотите сделать это, вам остается одно решение дочерние экземпляры.

...И, наконец, пример с некоторым результатом:

class A{

  function a_hi(){

    echo " Это класс A".PHP_EOL."<br>".PHP_EOL;  

  }

}

 

class B extends A{

  function b_hi(){

    echo " Это класс B".PHP_EOL."<br>".PHP_EOL;  

  }

}

 

class C extends B{

  function c_hi(){

    echo " Это класс C".PHP_EOL."<br>".PHP_EOL;  

  }

}

 

$myTestInstance = new C();

$myTestInstance->a_hi();

$myTestInstance->b_hi();

$myTestInstance->c_hi();

 

Вывод будет таким:

Это класс  A 

Это класс  B 

Это класс  C 

 

Ответ 8

Я прочитал несколько статей, в которых не поощряется наследование в проектах (в отличие от библиотек/фреймворков) и призывается программировать против интерфейсов, а не против реализаций.

Они также пропагандируют ОО путем композиции: если вам нужны функции в классах a и b, сделайте c, имеющий члены/поля этого типа:

class C {

    private $a, $b;

    public function __construct($x, $y) {

        $this->a = new A(42, $x);

        $this->b = new B($y);

    }

    protected function DoSomething() {

        $this->a->Act();

        $this->b->Do();

    }

}

 

Схожие статьи

Web

Неопределенные переменные в PHP - как их правильно определить

Web

Какой кэш PHP (код операции) следует использовать и почему?

Web

Определение размера удаленного файла без загрузки файла

Web

Преобразование сценария PHP в автономный исполняемый файл Windows

×