Могу ли я расширить класс, используя более 1 класса в PHP?


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

т. е. class a extends b extends c

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

20 114

20 ответов:

отвечая на ваше редактирование:

Если вы действительно хотите подделать множественное наследование, вы можете использовать магическую функцию __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;
  }

  // fake "extends C" using magic function
  public function __call($method, $args)
  {
    $this->c->$method($args[0]);
  }
}


$a = new A;
$a->method_from_b("abc");
$a->method_from_c("def");

печатает "abcdef"

вы не можете иметь класс, который наследует два базовых класса. Ты не мог этого сделать.

// this is NOT allowed (for all you google speeders)
Matron extends Nurse, HumanEntity

однако вы можете иметь иерархию следующим образом...

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable

и так далее.

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

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

Они признаны за их потенциал в поддержке лучшей композиции и повторного использования, следовательно, их интеграция в более новых версиях языков, таких как Perl 6, Squeak, Scala, Slate и Fortress. Черты также были перенесены на Java и C#.

дополнительная информация: https://wiki.php.net/rfc/traits

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

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

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

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 внутренне. Однако он не может получить доступ к любому частные / защищенные методы / переменные, такие как реальный расширенный класс.

Я прочитал несколько статей, препятствующих наследованию в проектах (в отличие от библиотек/фреймворков) и поощряющих Программирование интерфейсов agaisnt, нет против реализаций.
Они также защищают OO по составу: Если вам нужны функции в классах 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();
    }
}

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

точного ответа

нет вы не можете, соответственно, не очень, как руководство extends ключевое слово говорит:

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

ответ

однако, как правильно предположил @adam, это не запрещает вам использовать множественное иерархическое наследование.

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

так что довольно простой пример на этом будет:

class firstInheritance{}
class secondInheritance extends firstInheritance{}
class someFinalClass extends secondInheritance{}
//...and so on...

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

как вы могли заметить, вы можете только сделать несколько (2+) intehritance по иерархии, если у вас есть контроль над всеми классами, включенными в процесс - это означает, что вы не можете применить это решение, например, со встроенными классами или с классами, которые вы просто не можете редактировать-если вы хотите это сделать, вы остаетесь с экземплярами @Franck solution - child.

...И наконец пример с некоторым выводом:

class A{
  function a_hi(){
    echo "I am a of A".PHP_EOL."<br>".PHP_EOL;  
  }
}

class B extends A{
  function b_hi(){
    echo "I am b of B".PHP_EOL."<br>".PHP_EOL;  
  }
}

class C extends B{
  function c_hi(){
    echo "I am c of C".PHP_EOL."<br>".PHP_EOL;  
  }
}

$myTestInstance = new C();

$myTestInstance->a_hi();
$myTestInstance->b_hi();
$myTestInstance->c_hi();

выходы

I am a of A 
I am b of B 
I am c of C 
<?php
// what if we want to extend more then one class?

Abstract class ExtensionBridge
{
    // array containing all the extended classes
    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("This Method {$method} doesn't exists");
    }


}

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;
?>

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

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();

см. Теперь деловая женщина логически унаследовала бизнес и человека;

множественное наследование, кажется, работает на уровне интерфейса. Я сделал тест на php 5.6.1.

вот рабочий код:

<?php


interface Animal
{
    public function sayHello();
}


interface HairyThing
{
    public function plush();
}

interface Dog extends Animal, HairyThing
{
    public function bark();
}


class Puppy implements Dog
{
    public function bark()
    {
        echo "ouaf";
    }

    public function sayHello()
    {
        echo "hello";
    }

    public function plush()
    {
        echo "plush";
    }


}


echo PHP_VERSION; // 5.6.1
$o = new Puppy();
$o->bark();
$o->plush();
$o->sayHello(); // displays: 5.6.16ouafplushhello

Я не думал, что это возможно, но я наткнулся на исходный код SwiftMailer, в классе Swift_Transport_IoBuffer, который имеет следующее определение:

interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream

Я еще не играл с ним, но я подумал, что было бы интересно поделиться.

Я только что решил свою проблему "множественного наследования" с:

class Session {
    public $username;
}

class MyServiceResponsetype {
    protected $only_avaliable_in_response;
}

class SessionResponse extends MyServiceResponsetype {
    /** has shared $only_avaliable_in_response */

    public $session;

    public function __construct(Session $session) {
      $this->session = $session;
    }

}

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

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

вот краткий учебник для вас,http://culttt.com/2014/06/25/php-traits/

PHP еще не поддерживает множественное наследование классов, однако он поддерживает множественное наследование интерфейса.

см.http://www.hudzilla.org/php/6_17_0.php для некоторых примеров.

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

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

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

и "переместить" все классы, которые используют это иерархически вниз. Мне нужно переписать функции, которые являются специфическими.

Если вы хотите проверить, является ли функция общедоступной, см. Этот раздел:https://stackoverflow.com/a/4160928/2226755

и использовать call_user_func_array(...) метод для многих или нет аргументов.

такой :

class B {
    public function method_from_b($s) {
        echo $s;
    }
}

class C {
    public function method_from_c($l, $l1, $l2) {
        echo $l.$l1.$l2;
    }
}

class A extends B {
    private $c;

    public function __construct() {
        $this->c = new C;
    }

    public function __call($method, $args) {
        if (method_exists($this->c, $method)) {
            $reflection = new ReflectionMethod($this->c, $method);
            if (!$reflection->isPublic()) {
                throw new RuntimeException("Call to not public method ".get_class($this)."::$method()");
            }

            return call_user_func_array(array($this->c, $method), $args);
        } else {
            throw new RuntimeException("Call to undefined method ".get_class($this)."::$method()");
        }
    }
}


$a = new A;
$a->method_from_b("abc");
$a->method_from_c("d", "e", "f");

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

однако, много времени было бы полезно наследовать от нескольких классов. Например, может быть желательно наследовать методы от нескольких различных классов, чтобы предотвратить дублирование кода.

эта проблема может привести к классу, который имеет долгую историю семейного наследования что часто не имеет смысла.

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

черт

интерфейсы действительно бессмысленны, чтобы сформировать у меня логический смысл, поскольку они фактически заставляют меня снова и снова реализовывать один и тот же метод, он просто действует как напоминание или комментарий TODO. Не говоря уже о том, что это плохо, но логика заключается в том, чтобы избежать алмазной проблемы, о которой я думаю, что Python, вероятно, имеет лучшее решение для этого. Вам не нужно переписывать тело функции снова и снова. вы можете просто выбрать, какой Родительский вызывающий объект для выполнения.

например, если у вас есть класс Травоядное и класс плотоядное и класс всеядное все имеют функцию eat, которая принимает param пищу, в этом случае нам придется повторно реализовать eat во всеядном, чем просто заставить есть всеядное называть либо травоядное есть, либо плотоядное есть.

class Herbivore:
def eat(self, food):
    print("Food is supposed to be vegan")


class Carnivore:
def eat(self, food):
    print("Food is supposed to be meaty")



class Omnivore(Carnivore, Herbivore):
def eat(self, food):
    #Test the food type and call function for either 
    #Carnivore.eat(food)
    #Herbivore.eat(food)
    #There is no actual re-implementation of the action
    print("Food has to either bee meaty or vegan")

класс A расширяет B {}

класс B расширяет C {}

тогда A расширил и B и C