Симуляция множественного наследования.

Статус
В этой теме нельзя размещать новые ответы.

Vijon

Новичок
Симуляция множественного наследования.

Кто реализовывал сабж, поделитесь опытом, пожалуйста.
 

HraKK

Мудак
Команда форума
Кто симулировал слабоумие, поделитесь опытом.

Точно сегодняшняя ночь плохо влияет на всех, идитте спать шоли.
 

AmdY

Пью пиво
Команда форума
ищи и обрящешь
это обсуждалось мильон раз
 

Vijon

Новичок
AmdY

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

HraKK

Мудак
Команда форума
В общих чертах - не нужно этого хотеть, если вы этого хотите значит у вас не правильная архитектура приложения.
 

AmdY

Пью пиво
Команда форума
в левом верхнем углу находится форма поиска, введи "множественное наследование".
можно конечно в гугле или яндексе поискать, но в последнем меня Opera почему-то забанила :(
 

Vijon

Новичок
Ну вот, где-то так...

PHP:
class Multi
{
	public $_Exemplars_=array(); 
	public $_Classes_=array();
	public $_Dynamic_=array();
	public $_Child_=null;
	////
	////
	protected function __construct()
	{
		foreach ($this->_Classes_ as $Class=>$Args) 
		{
			switch ( count($Args) )
			{
				case 0:
					$this->_Exemplars_[$Class]=new $Class; 
					break;
				case 1:
					$this->_Exemplars_[$Class]=new $Class($Args[0]); 
					break;
				case 2:
					$this->_Exemplars_[$Class]=new $Class($Args[0], $Args[1]); 
					break;
				case 3:
					$this->_Exemplars_[$Class]=new $Class($Args[0], $Args[1], $Args[2]); 
					break;
				case 4:
					$this->_Exemplars_[$Class]=new $Class($Args[0], $Args[1], $Args[2], $Args[3]); 
					break;
				case 5:
					$this->_Exemplars_[$Class]=new $Class($Args[0], $Args[1], $Args[2], $Args[3], $Args[4]); 
					break;
			}
			////
			$this->_Exemplars_[$Class]->_Child_=$this;
		} 
  	}
 	////
 	////
	public function __get($_Key) 
    { 
		static $Searched;
		////
		if ( isset($this->_Exemplars_[$_Key]) ) 
		{
			$Searched=false;
			////
			return $this->_Exemplars_[$_Key];
		}
		////
		if ( isset($this->_Dynamic_[$_Key]) ) 
		{
			$Searched=false;
			////
			return $this->_Dynamic_[$_Key];
		}
		////
		if ( ($this->_Child_!=null) && !$Searched )
		{
			return $this->_Child_->$_Key;
		}
		////
		$Searched=true;
		////
		$Count=count($this->_Exemplars_);
		$Exemplar=end($this->_Exemplars_);
		for ($i=$Count-1; $i>=0; $i--)
		{
			if ( isset($Exemplar->$_Key) )
			{
				$Searched=false;
				////
				return $Exemplar->$_Key;
			}
			////
			$Result=$Exemplar->$_Key;
			////
			if ( !$Searched ) return $Result;
			////
			$Exemplar=prev($this->_Exemplars_);
		}
		////
		return;
  	}
 	////
 	////
	public function __set($_Key, $_Value) 
    { 
		static $Searched;
		////
		if ( isset($this->_Dynamic_[$_Key]) ) 
		{
			$Searched=false;
			////
			$this->_Dynamic_[$_Key]=$_Value;
			////
			return;
		}
		if ( ($this->_Child_!=null) && !$Searched )
		{
			$this->_Child_->$_Key=$_Value;
			////
			return;
		}
		////
		$Searched=true;
		////
		$Count=count($this->_Exemplars_);
		$Exemplar=end($this->_Exemplars_);
		for ($i=$Count-1; $i>=0; $i--)
		{
			if ( isset($Exemplar->$_Key) )
			{
				$Searched=false;
				////
				$Exemplar->$_Key=$_Value;
				////
				return;
			}
			////
			$Exemplar->$_Key=$_Value;
			////
			if ( !$Searched ) return;
			////
			$Exemplar=prev($this->_Exemplars_);
		}
	}
 	////
 	////
	public function __call($_Method, $_Args) 
    { 
		static $Searched;
		////
		if ( ($this->_Child_!=null) && !$Searched )
		{
			return call_user_func_array(array($this->_Child_, $_Method), $_Args);
		}
		////
		$Searched=true;
		////
		$Count=count($this->_Exemplars_);
		$Exemplar=end($this->_Exemplars_);
		for ($i=$Count-1; $i>=0; $i--)
		{
			if ( method_exists($Exemplar, $_Method) )
			{
				$Searched=false;
				////
				return call_user_func_array(array($Exemplar, $_Method), $_Args);
			}
			////
			$Result=call_user_func_array(array($Exemplar, $_Method), $_Args);
			////
			if ( !$Searched ) return $Result;
			////
			$Exemplar=prev($this->_Exemplars_);
		}
		////
		return;
    }
 }

class A1 extends Multi
{
	public function __construct()
	{
		Multi::__construct();
	}
	////
	////
	public function TestA1_1()
	{
		print("A1_1<br>");
	}
	////
	////
	public function TestA1_2()
	{
		print($this->TestA2_2());
	}
}

class A2 extends Multi
{
	public function __construct()
	{
		Multi::__construct();
	}
	////
	////
	public function TestA2_2()
	{
		print("A2_2<br>");
	}
}

class A3 extends Multi
{
	public function __construct()
	{
		Multi::__construct();
	}
}

class B1 extends Multi
{
	public function __construct()
	{
		$this->_Classes_=array(
								"A1"=>array(),
								"A2"=>array(),
								"A3"=>array()
							);
		////
		Multi::__construct();
	}
}

$B1=new B1;
$B1->TestA1_1();
$B1->TestA1_2();
Грубовато и наспех, конечно. Особенно, в конструкторе. Но работает. То есть, из классов, наследующих "Мульти", можно создать иерархию множественного наследования неограниченной сложности. При этом, каждый из классов иерархии может обращаться к открытым свойствам и методам любого другого. Также возможно вызывать методы "классов-предков"...

PHP:
$this->A->B->C->_Child_=null;
$this->A->B->C->ParentMethod();
$this->A->B->C->_Child_=$this->A->B;
-~{}~ 17.12.08 23:04:

Да... Из двух одноименных методов или свойств вызывается определенное позднее.
 

kirill538

Новичок
interface I_A {
function a();
}
interface I_B {
function b();
}

class Multi implements I_A, I_B {
function __construct(){
// создаем экземпляры классов, реализующих интерфейсы I_A и I_B
}
function a(){ return $this->a_class->a(); }
function b(){ return $this->b_class->b(); }
}

как-то так, вероятно.
 

x-yuri

Новичок
Vijon
ну, я бы наследников в конструкторе с помощью eval создавал либо сделал у них конструктор __construct(array $params)

алгоритм, конечно, убийственный, $Searched используется, как 1) индикатор что мы искали от самого нижнего наследника, 2) что мы нашли что нужно ($Searched == FALSE).
И эта переменная должна быть восстановлена в исходное положение (NULL/FALSE)

и сам метод (__get) у тебя и находит нижнего наследника и ищет

я бы как-то так сделал

PHP:
class Multi 
{ 
    public $_Instances_ = array();  
    public $_Child_ = null; 


    protected function __construct( $classes=NULL ) { 
    	
    	if (! $classes)
    		return;
        foreach( $classes as $class=>$args ) { 
        	if( is_string($args) ) {                # нет параметров, только имя класса
        		$this->_Instances_[$args] = new $args();
				$this->_Instances_[$args]->_Child_ = $this; 
        	} else {
	        	foreach( $args as &$arg )
	        		$arg = var_export($arg, TRUE);
	        	eval( '$this->_Instances_["'.$class.'"] = new '.$class.'('.implode(',', $args).');' );
	            $this->_Instances_[$class]->_Child_ = $this; 
        	}
        }  
    } 
	
	
    public function __get( $k )  
    {  
    	$child = $this;
    	while( $child->_Child_ )
    		$child = $this->_Child_;
    	if (! $child)
    		$child = $this;
    	return $this->_find( $k );
    } 
    
    
    protected function _find( $k ) {
    	
    	foreach( $this->_Instances_ as $inst ) {
    		$r = $inst->_find( $k );
    		if( isset($r) )
    			return $r;
    	}
    	if( isset($this->$k) )
    		return $this->$k;
    }
    
      
	public function get( $k, $scope='' ) {
		
		$classes = explode( '::', $scope );
		$o = $this;
		foreach( $classes as $class )
			if( $o->_Instances_[ $class ] )
				$o = $o->_Instances_[ $class ];
			else 
				return;
		return $o->_find( $k );
	}
} 

class A1 extends Multi 
{ 
	public $a = 1;
    public function __construct() 
    { 
        Multi::__construct(); 
    } 
} 

class A2 extends Multi 
{ 
	public $a = 2;
    public function __construct() 
    { 
        Multi::__construct(); 
    } 
} 
class B1 extends Multi 
{ 
    public function __construct() 
    { 
        Multi::__construct(array( 'A1', 'A2' => array('A2_arg') ));
    } 
} 

$B1=new B1; 
var_dump($B1->a);
var_dump($B1->get('a', 'A2'));
это только по поводу свойств. Можно добавить метод parent, который возвращает одного из родителей. Ну и могут быть ошибки, особо не проверял.

а вообще
В общих чертах - не нужно этого хотеть, если вы этого хотите значит у вас не правильная архитектура приложения.
есть в этом что-то, а тебе множественное наследование зачем, а?

-~{}~ 18.12.08 06:11:

интерфейсов обычно хватает

-~{}~ 18.12.08 06:59:

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

HraKK

Мудак
Команда форума
x-yuri
eval нельзя использовать никогда, андестанд?

И харе придумывать свои четырех угольные велики. Множественное наследование НЕ НУЖНО. Меняйте архитектуру раз встретили нужду, а не пришивайте 3-ю руку пациенту.
 

Vijon

Новичок
для x-yuri

Ваш алгоритм не кажется мне удачным. Во-первых, евал, думается, ничем не лучше свитча.

Во-вторых и в главных, Ваш алгоритм не выполняет поставленную задачу. У меня, ВСЕ методы и свойства ВСЕХ входящих в иерархию классов образуют единое пространство имен. К любому из них можно обращаться явно, не задумываясь о том, к какому классу он принадлежит. Поэтому, кстати, и поиск идет от корня иерархии, а как иначе?

У Вас же, необходимо помнить к какому именно классу-предку относится вызываемый метод или свойство. Боюсь, это весьма усложнит задачу проектировщика. Кроме того, Вы сами заметили проблему с гет. У меня никаких таких проблем не наблюдается. Кто выше по иерархии того и возвращают.

А метод пэрэнт я и сам думаю дописать. Здесь я с Вами совершенно согласен.
 

serglt

Анус, ой, Ахтунг
Нда.. Тоесть к какому объекту относится свойство/метод запомнить трудно, а все свойства/методы помнить для всех объектов - нет. если у тебя в объектах будет одно и тоже свойство или метод, из какого объекта брать/вызывать? Или ты будешь уникально называть методы/свойства для каждого класса?
О придумал! Ты в конструкторе пробегай по всем свойствам и методам и проверяй на повторения если что - ошибка "duplicated method or property in class1 and class2". :-D
Класс Диск и класс Резина это предки класса Колесо, тебе нужно достать ширину, у них она разная.
Оно тебе точно надо? :)
 

Vijon

Новичок
если у тебя в объектах будет одно и тоже свойство или метод, из какого объекта брать/вызывать?
Как я уже писал по этому поводу, вызывается то свойство или метод, которые были определены позднее. По этому же принципу, кстати, строится аналогичная иерархия в Ява-Скрипте, естественно, встроенными средствами.

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

-~{}~ 18.12.08 14:13:

Класс Диск и класс Резина это предки класса Колесо, тебе нужно достать ширину, у них она разная.
Оно тебе точно надо?
Ширина Колеса, которая меня будет интересовать, совпадает с Шириной Резины, насколько я понимаю (я не автомобилист). Значит, Резина будет определяться последней. Если же я захочу узнать Ширину Диска, то сделаю это через метод Пэрент.
 

HraKK

Мудак
Команда форума
Пациент клинически болен. Причем походу неизлечимо.
 
Статус
В этой теме нельзя размещать новые ответы.
Сверху