Pregunta

Si tengo varias clases con funciones que necesito pero quiero almacenar por separado para la organización, ¿puedo extender una clase para tener ambas cosas?

es decir class a extends b extends c

EDITAR: Sé cómo extender las clases uno a la vez, pero estoy buscando un método para extender instantáneamente una clase usando múltiples clases base: Afaik no puede hacerlo en PHP, pero debería haber formas de evitarlo sin recurrir class c extends b, class b extends a

¿Fue útil?

Solución

Respondiendo a su edición:

Si realmente desea fingir herencia múltiple, puede usar la función mágica __call ().

Esto es feo, aunque funciona desde el punto de vista del usuario de Clase 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");

Imprime "ABCDEF"

Otros consejos

No puede tener una clase que extienda dos clases base. No podrías tener.

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

Sin embargo, podrías tener una jerarquía de la siguiente manera ...

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable

y así.

Puede usar rasgos, que, con suerte, estarán disponibles en PHP 5.4.

Los rasgos es un mecanismo para la reutilización de código en lenguajes de herencia única, como PHP. Un rasgo está destinado a reducir algunas limitaciones de herencia única al permitir que un desarrollador reutilice conjuntos de métodos libremente en varias clases independientes que viven en diferentes jerarquías de clase. La semántica de la combinación de rasgos y clases se define de una manera, lo que reduce la complejidad y evita los problemas típicos asociados con la herencia y las mezclas múltiples.

Son reconocidos por su potencial para apoyar una mejor composición y reutilización, de ahí su integración en versiones más nuevas de idiomas como Perl 6, Squeak, Scala, Slate y Fortress. Los rasgos también se han portado a Java y C#.

Más información: https://wiki.php.net/rfc/traits

Las clases no están destinadas a ser solo colecciones de métodos. Se supone que una clase representa un concepto abstracto, con el estado (campos) y el comportamiento (métodos) que cambia el estado. El uso de la herencia solo para obtener un comportamiento deseado suena como un mal diseño de OO, y exactamente la razón por la cual muchos idiomas rechazan la herencia múltiple: para evitar la "herencia de espagueti", es decir, extender 3 clases porque cada uno tiene un método que necesita y terminar con Una clase que hereda 100 métodos y 20 campos, pero solo usa 5 de ellos.

Creo que hay planes para agregar mezclas pronto.

Pero hasta entonces, ve con la respuesta aceptada. Puede abstraer un poco para hacer una clase "extensible":

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

Tenga en cuenta que en este modelo "OtherClass" llega a 'saber' sobre $ Foo. OtherClass necesita tener una función pública llamada "SetExtendee" para configurar esta relación. Luego, si sus métodos se invocan desde $ foo, puede acceder a $ foo internamente. Sin embargo, no obtendrá acceso a ningún método/variables privado/protegido como lo haría una clase extendida real.

La respuesta actualmente aceptada de @Franck funcionará, pero de hecho no es una herencia múltiple, sino una instancia infantil de clase definida fuera de alcance, también existe el __call() taquigrafía: considere usar solo $this->childInstance->method(args) en cualquier lugar donde necesite el método de clase de clases externos en la clase "extendida".

Respuesta exacta

No, no puedes, respectivamente, en realidad no, como manual de extends palabra clave dice:

Una clase extendida siempre depende de una sola clase base, es decir, la herencia múltiple no es compatible.

Respuesta real

Sin embargo, como @adam sugirió correctamente, esto no le prohíbe usar múltiples herencias jerárquicas.

Puedes extender una clase, con otra y otra con otra y así sucesivamente ...

Tan un ejemplo bastante simple en esto sería:

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

Nota IMPORTANTE

Como habrás notado, Solo puede hacer múltiples (2+) entrada por jerarquía si tiene control sobre todas las clases incluidas en el proceso - Eso significa que no puede aplicar esta solución, por ejemplo, con clases incorporadas o con clases que simplemente no puede editar; si desea hacerlo, se queda con la solución @Franck: instancias infantiles.

... y finalmente ejemplo con alguna salida:

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

Que sale

I am a of A 
I am b of B 
I am c of C 

Use rasgos como clases base. Luego úsalos en una clase principal. Extiéndelo .

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

Vea ahora Mujer de Negocios Heredados Lógicamente Empresarios y Humanos Ambos;

<?php
// what if we want to extend more than 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;
?>

He leído varios artículos que desalentan la herencia en proyectos (a diferencia de las bibliotecas/marcos) y alentaban a programar interfaces de Agaisnt, no contra las implementaciones.
También abogan por composición: si necesita las funciones en la clase A y B, haga que C tenga miembros/campos de este tipo:

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

La herencia múltiple parece funcionar a nivel de interfaz. Hice una prueba en PHP 5.6.1.

Aquí hay un código de trabajo:

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

No pensé que eso fuera posible, pero me topé con el código fuente de Swiftmailer, en la clase Swift_Transport_ioBuffer, que tiene la siguiente definición:

interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream

Todavía no jugué con eso, pero pensé que sería interesante compartir.

Acabo de resolver mi problema de "herencia múltiple" con:

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

}

De esta manera, tengo el poder de manipular la sesión dentro de una respuesta de sesión que extiende myServiceReSponseSype que aún puede manejar la sesión por sí misma.

Puede hacerlo usando rasgos en PHP que se anunciaron a partir de PHP 5.4

Aquí hay un tutorial rápido para ti, http://culttt.com/2014/06/25/php-traits/

PHP aún no admite la herencia de clase múltiple, sin embargo, admite la herencia de interfaz múltiple.

Ver http://www.hudzilla.org/php/6_17_0.php para algunos ejemplos.

PHP no permite la herencia múltiple, pero puede hacerlo implementando múltiples interfaces. Si la implementación es "pesada", proporcione implementación esquelética para cada interfaz en una clase separada. Entonces tú puedes Delegar toda la clase de interfaz a estas implementaciones esqueléticas a través de contención de objetos.

Sin saber exactamente lo que está tratando de lograr, sugeriría investigar la posibilidad de rediseñar su aplicación para usar la composición en lugar de la herencia en este caso.

Siempre la buena idea es hacer la clase de los padres, con funciones ... es decir, agregar toda esta funcionalidad a los padres.

Y "mover" todas las clases que usan esto jerárquicamente hacia abajo. Necesito: reescribir funciones, que son específicas.

Si desea verificar si una función es pública, consulte este tema: https://stackoverflow.com/a/4160928/2226755

Y use el método Call_user_func_array (...) para muchos argumentos o no.

Como esto :

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

Uno de los problemas de PHP como lenguaje de programación es el hecho de que solo puede tener una herencia única. Esto significa que una clase solo puede heredar de otra clase.

Sin embargo, muchas veces sería beneficiosa heredar de múltiples clases. Por ejemplo, podría ser deseable heredar métodos de un par de clases diferentes para evitar la duplicación de código.

Este problema puede conducir a una clase que tiene un largo historial familiar de herencia que a menudo no tiene sentido.

En PHP 5.4, se agregó una nueva característica del lenguaje conocida como rasgos. Un rasgo es como una mezcla en la que le permite mezclar clases de rasgos en una clase existente. Esto significa que puede reducir la duplicación de código y obtener los beneficios mientras evita los problemas de herencia múltiple.

Rasgos

La clase A se extiende B {}

La clase B se extiende C {}

Entonces A ha extendido tanto B como C

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top