Если у меня есть несколько классов с функциями, которые мне нужны, но я хочу хранить их отдельно для организации, могу ли я расширить класс, чтобы иметь и то, и другое?
то есть class a extends b extends c
изменить: я знаю, как расширять классы по одному, но я ищу метод мгновенного расширения класса с использованием нескольких базовых классов - AFAIK вы не можете сделать это в PHP, но должны быть способы обойти это, не прибегая к class c extends b, class b extends a
Я изучаю интерфейсы, так как я не большой поклонник больших иерархий классов. Но я не понимаю, как интерфейсы на самом деле что-то делают?
Интерфейсы позволяют «наследовать» только API, но не тела функций. Он заставляет класс a реализовать методы из интерфейса b и c. Это означает, что если вы хотите наследовать поведение, вы должны агрегировать объекты-члены классов b и c в своем классе a.
Я имею в виду, поместите частный $ b (экземпляр b) и частный $ c (экземпляр c) в свой класс a, если это было недостаточно ясно.
Рассмотрите возможность использования декораторов sourcemaking.com/design_patterns/decorator или стратегий sourcemaking.com/design_patterns/strategy
Я бы поставил под сомнение свой дизайн, если считаю, что множественное наследование - лучшее решение моей проблемы. Я побежал по этой дороге и наткнулся на этот вопрос. Как правило, если вы пытаетесь сделать что-то, что не поддерживается языком, вам следует подвергнуть сомнению свой дизайн. Множественное наследование кажется излишне запутанным и неуклюжим.
Пожалуйста, рассмотрите черты как правильный ответ.






PHP еще не поддерживает множественное наследование классов, однако он поддерживает множественное наследование интерфейсов.
См. Некоторые примеры в http://www.hudzilla.org/php/6_17_0.php.
Все же? Я сомневаюсь, что они это сделают. Современные объектно-ориентированные объекты не поощряют множественное наследование, оно может быть беспорядочным.
У вас не может быть класса, расширяющего два базовых класса. Вы не могли этого сделать.
// 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
и так далее.
Можете ли вы объяснить, почему правильно сначала унаследовать Медсестру в Матрону, а затем объявить наследование Человеческой сущности в Медсестре?
@Qwerty Потому что у Матрона есть дополнительные качества медсестры, а у медсестры - все качества человека. Следовательно, Матрона - человек-медсестра и, наконец, имеет возможности Матрона.
Классы не должны быть просто коллекциями методов. Предполагается, что класс представляет абстрактное понятие, как с состоянием (полями), так и поведением (методами), которое изменяет состояние. Использование наследования только для получения желаемого поведения звучит как плохой объектно-ориентированный дизайн, и именно по этой причине многие языки запрещают множественное наследование: чтобы предотвратить «наследование спагетти», то есть расширение 3 классов, потому что у каждого есть метод, который вам нужен, и в итоге класс, который наследует 100 методов и 20 полей, но использует только 5 из них.
Я не согласен с вашим утверждением, что запрос OP составляет "плохой объектно-ориентированный дизайн"; просто посмотрите на появление Миксины, чтобы поддержать позицию, согласно которой добавление методов к классу из нескольких источников является хорошей архитектурной идеей. Тем не менее, я сообщу вам, что PHP не обеспечивает оптимальный набор языковых функций для достижения оптимального «дизайна», но это не означает, что использование доступных функций для его аппроксимации обязательно является плохой идеей; просто посмотрите на ответ @Franck.
Если вы действительно хотите имитировать множественное наследование в PHP 5.3, вы можете использовать волшебную функцию __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"
На самом деле мне очень нравится идея расширения класса. Существуют ли какие-либо известные ограничения в том, чтобы делать это таким образом?
Насколько я знаю, без ограничений, PHP - очень разрешительный язык для таких небольших хаков. :) Как отмечали другие, это не правильный способ ООП.
Мне нравится эта идея, чистая и простая!
Вы не сможете использовать защищенные или частные методы.
Есть комментарий, который предлагает способ сделать это в php doc, с тем же ограничением для защищенных и частных методов php.net/manual/fr/keyword.extends.php#98665
@wormhit, однако, я бы не рекомендовал его для использования в производстве, можно использовать ReflectionClass для доступа к частным и защищенным методам.
Это не сработает для Implements, которые ожидают, что метод действительно существует, и поведение в значительной степени не определено, когда несколько базовых классов имеют метод с тем же именем. Это также испортит способность вашего редактора давать вам подсказки.
Я не совсем понимаю, почему сообщество проголосовало за этот ответ. инициализация объекта C - это далеко не то же самое, что расширение класса. С помощью этого метода вы отказываетесь от поддержки видимости свойств и наследования объектов. Если бы я увидел, что кодер написал это в производственном коде, я бы немедленно уволил его. Единственный случай или сценарий, когда подобный код следует считать приемлемым, - это если вы пишете описательный объект, и в этом случае он, вероятно, должен быть статическим, если вы не создаете экземпляры объекта в памяти.
@wormhit, чтобы сохранить защищенную или частную видимость, вам нужно будет расширить расширенный класс. В большинстве случаев это не идеально.
PHP не допускает множественного наследования, но вы можете реализовать несколько интерфейсов. Если реализация «тяжелая», предоставьте скелетная реализация для каждого интерфейса в отдельном классе. Затем вы можете делегировать весь интерфейсный класс этим скелетным реализациям через сдерживание объекта.
Я прочитал несколько статей, не одобряющих наследование в проектах (в отличие от библиотек / фреймворков) и поощряющих программирование интерфейсов, не противоречащих реализации. Они также защищают объектно-ориентированный подход по составу: если вам нужны функции в классе 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();
}
}
Фактически это становится тем же, что показывают Франк и Сэм. Конечно, если вы решите явно использовать композицию, вы должны использовать внедрение зависимости.
Я полагаю, что в ближайшее время есть планы по добавлению микшеров.
Но до тех пор оставайтесь с принятым ответом. Вы можете немного абстрагироваться от этого, чтобы создать «расширяемый» класс:
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 изнутри. Однако он не получит доступа к каким-либо частным / защищенным методам / переменным, как настоящий расширенный класс.
Не зная точно, чего вы пытаетесь достичь, я бы посоветовал изучить возможность перепроектирования вашего приложения для использования в этом случае композиции, а не наследования.
Всегда хорошая идея - создать родительский класс с функциями ... т.е. добавить все эти функции в родительский.
И «переместите» все классы, которые используют это иерархически, вниз. Мне нужно - переписать функции, которые специфичны.
Вы можете использовать трейты, которые, надеюсь, будут доступны в PHP 5.4.
Traits - это механизм для повторного использования кода в языках с единичным наследованием, таких как PHP. Признак предназначен для уменьшения некоторых ограничений одиночного наследования, позволяя разработчику свободно повторно использовать наборы методов в нескольких независимых классах, находящихся в разных иерархиях классов. Семантика комбинации Traits и классов определяется способом, который снижает сложность и позволяет избежать типичных проблем, связанных с множественным наследованием и Mixins.
Они известны своим потенциалом в поддержке лучшей композиции и повторного использования, следовательно, их интеграция в новые версии языков, таких как Perl 6, Squeak, Scala, Slate и Fortress. Черты также были перенесены на Java и C#.
Дополнительная информация: https://wiki.php.net/rfc/traits
Постарайтесь не злоупотреблять ими. По сути, это статические функции, которые вы применяете к объектам. Вы можете создать беспорядок, злоупотребляя ими.
Не могу поверить, что это не выбранный ответ.
Я только что решил проблему с "множественным наследованием":
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, по-прежнему имея возможность обрабатывать сеанс самостоятельно.
Если вы хотите проверить, является ли функция общедоступной, см. Эту тему: 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
// 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;
?>
Ваш ответ должен содержать объяснение вашего кода и описание того, как он решает проблему.
Это почти самоочевидно, но было бы здорово иметь комментарии по коду.
теперь, если у Ext1 и Ext2 есть функция с тем же именем, всегда будет вызываться Ext1 c, она вообще не будет зависеть от параметров.
Начиная с PHP 5.4.0 есть "Черты" - вы можете использовать больше трейтов в одном классе, поэтому окончательный момент принятия решения будет заключаться в том, хотите ли вы действительно наследство или вам просто нужен какая-то "особенность" (черта). Тенденция - это, расплывчато сказано, уже реализованный интерфейс, который должен быть просто used.
Нет, соответственно, не совсем, как говорит руководство по ключевому слову extends:
An extended class is always dependent on a single base class, that is, multiple inheritance is not supported.
Однако, как правильно предположил @adam, это НЕ запрещает вам использовать множественное иерархическое наследование.
Вы МОЖЕТЕ расширить один класс другим, а другой - другим и так далее ...
Вот довольно простой пример:
class firstInheritance{}
class secondInheritance extends firstInheritance{}
class someFinalClass extends secondInheritance{}
//...and so on...
Как вы могли заметить, вы можете выполнять множественное (2+) взаимодействие по иерархии только в том случае, если у вас есть контроль над всеми классами, включенными в процесс. - это означает, что вы не можете применить это решение, например. со встроенными классами или с классами, которые вы просто не можете редактировать - если вы хотите это сделать, у вас останется решение @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
Вы можете сделать это с помощью Traits в PHP, объявленных в PHP 5.4.
Вот краткое руководство для вас, http://culttt.com/2014/06/25/php-traits/
Множественное наследование работает на уровне интерфейса. Сделал тест на 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
Я еще не играл с этим, но подумал, что, возможно, будет интересно поделиться.
интересно и неактуально.
Одна из проблем PHP как языка программирования заключается в том, что у вас может быть только одиночное наследование. Это означает, что класс может наследовать только от одного другого класса.
Однако в большинстве случаев было бы полезно наследовать от нескольких классов. Например, может быть желательно наследовать методы от пары разных классов, чтобы предотвратить дублирование кода.
Эта проблема может привести к классу, который имеет долгую семейную историю наследования, что часто не имеет смысла.
В PHP 5.4 была добавлена новая функция языка, известная как Traits. Trait похож на Mixin в том смысле, что он позволяет вам смешивать классы Trait в существующий класс. Это означает, что вы можете уменьшить дублирование кода и получить преимущества, избегая проблем множественного наследования.
Используйте черты характера как базовые классы. Затем используйте их в родительском классе. Расширьте это.
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.3, нет поддержки трейтов: D
Почему бы не использовать эту черту в BusinessWoman вместо BusinessPerson? Тогда у вас может быть фактическое множественное наследование.
@SOFe, потому что BusinessMan также может его расширить. Таким образом, любой, кто когда-либо занимается бизнесом, может расширять бизнес и автоматически получать черты характера.
То, как вы это делаете, ничем не отличается от того, что возможно при одиночном наследовании, когда BusinessPerson расширяет абстрактный класс под названием human. Я хочу сказать, что вашему примеру действительно не требуется множественное наследование.
Я верю, что до BusinessPerson это было необходимо, BusinessWoman могла напрямую унаследовать другие черты. Но здесь я попробовал сохранить обе черты в классе-посреднике, а затем использовать его там, где это необходимо. Таким образом, я могу забыть о включении обеих черт, если снова понадобится где-то еще (как я описал BusinessMan). Все дело в стилях кода. Если вы хотите включить прямо в свой класс. вы можете пойти дальше - так как вы абсолютно правы в этом.
Дело не в том, требуется ли это. Дело в том, что ваш пример, независимо от его значения на уровне бизнес-логики, вообще не демонстрирует множественное наследование.
класс A расширяет B {}
класс B расширяет C {}
Тогда A расширил и B, и C
@rostamiani, потому что этот метод также изменяет класс B. В большинстве случаев мы хотим иметь два несвязанных класса B и C (вероятно, из разных библиотек) и одновременно унаследоваться от A, так что A содержит все как от B, так и от C, но B не содержит ничего из C и наоборот.
Это не настоящий ответ, у нас есть повторяющийся класс
... но работает :)
class A {
//do some things
}
class B {
//do some things
}
class copy_B копирует весь класс B
class copy_B extends A {
//do some things (copy class B)
}
class A_B extends copy_B{}
Теперь
class C_A extends A{}
class C_B extends B{}
class C_A_b extends A_B{} //extends A & B
Используйте агрегацию или интерфейсы. В PHP не существует множественного наследования.