Domanda

Se dispongo di diverse classi con funzioni di cui ho bisogno ma che desidero archiviare separatamente per l'organizzazione, posso estendere una classe per averle entrambe?

cioè. class a extends b extends c

modificare:So come estendere le classi una alla volta, ma sto cercando un metodo per estendere istantaneamente una classe utilizzando più classi base - AFAIK non puoi farlo in PHP ma dovrebbero esserci dei modi per aggirarlo senza ricorrere a class c extends b, class b extends a

È stato utile?

Soluzione

Risposta tua modifica:

Se si vuole veramente falso ereditarietà multipla, è possibile utilizzare la funzione di magia __call ().

Questa è brutto se funziona dalla classe punto di vista di un utente:

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

Stampe "abcdef"

Altri suggerimenti

Non si può avere una classe che estende due classi di base. Non si potrebbe avere.

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

Si potrebbe tuttavia avere una gerarchia come segue ...

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable

e così via.

Si potrebbe utilizzare tratti, che, si spera, sarà disponibile da PHP 5.4.

Tratti è un meccanismo per il riutilizzo del codice in linguaggi di successione singoli come PHP. Un tratto destinato a ridurre alcune limitazioni di ereditarietà singola consentendo uno sviluppatore di riutilizzare gruppi di metodi liberamente in diverse classi indipendenti vivono in diverse gerarchie di classi. La semantica della combinazione di tratti e classi è definito in un modo, che riduce la complessità ed evita i problemi tipici associati con l'ereditarietà multipla e Mixin.

Sono riconosciuti per il loro potenziale nel sostenere una migliore composizione e il riutilizzo, quindi la loro integrazione nelle versioni più recenti di linguaggi come Perl 6, Squeak, Scala, Slate e Fortezza. Tratti sono stati portati su Java e C #.

Più informazioni: https://wiki.php.net/rfc/traits

Le classi non sono destinate ad essere solo raccolte di metodi. Una classe dovrebbe rappresentare un concetto astratto, sia con lo stato (campi) e comportamenti (metodi), che modifica lo stato. Utilizzando l'ereditarietà solo per ottenere qualche comportamento desiderato suona come progettazione OO male, ed esattamente il motivo per cui molte lingue non consentire l'ereditarietà multipla: al fine di evitare che "gli spaghetti eredità", vale a dire che si estende 3 classi, perché ognuno ha un metodo che è necessario, e finire con una classe che eredita 100 metodo e 20 campi, ma sempre e solo utilizza 5 di loro.

Ci sono piani per l'aggiunta di mix-ins presto, credo.

Ma fino ad allora, vai con la risposta accettata. Puoi astratto che un po 'per fare una classe "estendibile":

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

Si noti che in questo modello "OtherClass" arriva al 'sapere' di circa $ pippo. OtherClass ha bisogno di avere una funzione pubblica chiamata "setExtendee" per impostare questa relazione. Poi, se si tratta di metodi sono invocati da $ pippo, si può accedere a $ pippo internamente. Non sarà, tuttavia, ottenere l'accesso a qualsiasi / metodi protetti / variabili private come una vera e propria classe estesa farebbe.

La risposta attualmente accettata da @Franck funzionerà ma in realtà non si tratta di ereditarietà multipla ma di un'istanza figlio di classe definita fuori ambito, inoltre c'è il __call() abbreviazione: considera l'utilizzo di just $this->childInstance->method(args) ovunque sia necessario il metodo della classe ExternalClass nella classe "estesa".

Risposta esatta

No, non puoi, rispettivamente, non proprio, come manuale di extends parola chiave dice:

Una classe estesa dipende sempre da una singola classe di base, ovvero non è supportata l'eredità multipla.

Risposta vera

Tuttavia, come suggerito correttamente da @adam, ciò NON vieta di utilizzare l'ereditarietà gerarchica multipla.

PUOI estendere una lezione, con un'altra e un'altra con un'altra e così via...

Quindi un esempio piuttosto semplice su questo sarebbe:

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

Nota importante

Come avrai notato, puoi eseguire più interazioni (2+) per gerarchia solo se hai il controllo su tutte le classi incluse nel processo - ciò significa che non è possibile applicare questa soluzione, ad es.con classi integrate o con classi che semplicemente non puoi modificare: se vuoi farlo, ti rimane la soluzione @Franck: istanze figlio.

...E infine un esempio con qualche output:

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

Quali uscite

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

Utilizzare i tratti come classi di base. Poi utilizzarli in una classe genitore. Estenderlo.

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

Vedere affari adesso donna d'affari logicamente ereditato e umana di entrambi;

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

Ho letto diversi articoli scoraggiare eredità in progetti (al contrario di biblioteche / quadri), e incoraggiante per programmare interfacce agaisnt, non contro le implementazioni.
Sostengono anche OO dalla composizione: se avete bisogno delle funzioni in classe A e B, rendono c avendo membri / campi di questo 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();
    }
}

L'ereditarietà multipla sembra funzionare a livello di interfaccia. Ho fatto un test su php 5.6.1.

Ecco un codice di lavoro:

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

non pensavo che fosse possibile, ma mi sono imbattuto nel codice sorgente SwiftMailer, nella classe Swift_Transport_IoBuffer, che ha la seguente definizione:

interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream

Io non ho giocato con esso ancora, ma ho pensato che potrebbe essere interessante da condividere.

Ho appena risolto il mio problema "l'ereditarietà multipla" 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;
    }

}

In questo modo ho il potere di manipolare la sessione all'interno di un SessionResponse che si estende MyServiceResponsetype ancora in grado di gestire da sola sessione.

Siete in grado di farlo usando Tratti in PHP che ha annunciato a partire da PHP 5.4

Ecco un breve tutorial per voi, http://culttt.com/2014 / 06/25 / php-tratti /

PHP non supporta ancora l'ereditarietà multipla classe, ha tuttavia supporta l'ereditarietà multipla interfaccia.

http://www.hudzilla.org/php/6_17_0.php per alcuni esempi.

PHP non consente l'ereditarietà multipla, ma si può fare con la realizzazione interfacce multiple. Se l'implementazione è "pesante", fornire scheletrico realizzazione per ogni interfaccia in una classe separata. Poi, puoi delegato tutte le classi di interfaccia a queste implementazioni scheletrici via oggetto contenimento .

Non sapendo esattamente cosa si sta cercando di ottenere, vorrei suggerire esaminando la possibilità di ridisegnare voi l'applicazione da usare composizione piuttosto che eredità in questo caso.

Sempre buona idea è quella di rendere classe genitore, con le funzioni ... cioè aggiungere questo tutte le funzionalità di genitore.

E "spostare" tutte le classi che utilizzano questo gerarchicamente verso il basso. Ho bisogno - funzioni di riscrittura, che sono specifici

.

Se si vuole verificare se una funzione è pubblica vedere questo argomento: https://stackoverflow.com/a/4160928/2226755

E utilizzare il metodo call_user_func_array (...) per molti o meno argomenti.

In questo modo:

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 dei problemi di PHP come linguaggio di programmazione è il fatto che si può avere solo l'ereditarietà singola. Ciò significa che una classe può ereditare da una sola classe.

Tuttavia, un sacco di tempo che sarebbe vantaggioso per ereditare da più classi. Ad esempio, potrebbe essere auspicabile per ereditare metodi da un paio di classi diverse al fine di evitare la duplicazione del codice.

Questo problema può portare a classe che ha una lunga storia familiare di eredità che spesso non ha senso.

In PHP 5.4 una nuova caratteristica del linguaggio è stato aggiunto noto come Tratti. Un tratto è un po 'come un Mixin in quanto permette di mixare le classi Trait in una classe esistente. Questo significa che è possibile ridurre la duplicazione del codice e ottenere i benefici, evitando i problemi di ereditarietà multipla.

Tratti

classe A estende B {}

Classe B estende C {}

Allora A ha esteso sia B che C

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top