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

StackOverflow https://stackoverflow.com/questions/356128

  •  21-08-2019
  •  | 
  •  

Вопрос

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

т. е. class a extends b extends c

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

Это было полезно?

Решение

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

Если вы действительно хотите подделать множественное наследство, вы можете использовать функцию Magic __call ().

Это уродливо, хотя работает с точки зрения пользователя класса А:

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.

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

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

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

Занятия не предназначены для того, чтобы быть просто коллекциями методов. Предполагается, что класс представляет собой абстрактную концепцию с состоянием (поля) и поведения (методов), которые меняют состояние. Использование наследования просто для того, чтобы получить желаемое поведение, звучит как плохой дизайн, и именно причина, по которой многие языки запрещают множественное наследство: для предотвращения «наследования спагетти», то есть расширение 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();

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

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

Точный ответ

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

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

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

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

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

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

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

Важная заметка

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

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

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 

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

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

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

Множественное наследство, кажется, работает на уровне интерфейса. Я сделал тест на 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;
    }

}

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

Вы можете сделать это, используя функции в 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 была добавлена новая функция языка, известная как Traits.Признак чем-то похож на Миксин в том смысле, что он позволяет вам смешивать классы признаков с существующим классом.Это означает, что вы можете уменьшить дублирование кода и получить преимущества, избегая при этом проблем множественного наследования.

Трейты

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

Класс B расширяет c {}

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top