необходимо множественное наследие

Духовность™

Продвинутый новичок
необходимо множественное наследие

Столкнулся с ситуацией, когда необходимо множественное наследие. Иначе будет плодиться код.

Что делать?
 

Духовность™

Продвинутый новичок
класс машина
класс грузовая машина extends "класс машина"
класс грузовая машина с прицепом extends "класс грузовая машина" (и тут должно быть extends "класс машина")

что тут пересматривать? пересматривать тут нечего. придется из "класс грузовая машина" тупо копировать методы и свойства в "класс грузовая машина с прицепом".

:(
 

tony2001

TeaM PHPClub
B extends A
C extends B - это значит, что C является полноценным инстансом и A, и B, никаких дополнительных extends не надо.

PHP:
class A {} 
class B extends A {} 
class C extends B {} 
$c = new C; 
var_dump($c instanceof A); //true
 

Подрыв мозга

Новичок
Автор оригинала: triumvirat
что тут пересматривать? пересматривать тут нечего. придется из "класс грузовая машина" тупо копировать методы и свойства в "класс грузовая машина с прицепом".
:(
Если нет множественного наследования используется наследование интерфейсов (и/или) аггрегация. Прицеп не является частью машины, введите свойство прицеп в ваш класс Машина и устанавливайте его в null если прицепа нет.
 

rotoZOOM

ACM maniac
В твоем случае класс "грузовая машина" наследуется от "машина", а "грузовая машина с прицепом" от класса "грузовая машина" (уже Тони сказал).
Но как советуют умные люди лучше не применять наследование там, где без этого можно обойтись, а использовать декомпозицию и наследование от интерфейсов (Подрыв мозга это обозначил).
 

Духовность™

Продвинутый новичок

surg30n

Новичок
Заранее простите, что лезу не в свою песочницу, но все же - интересует похожий вопрос.

Есть классы (модели):

bug extends mdl_item
product extends mdl_item

Классы имплементирующие нужный функционал для моделей:

taggable_item
commentable_item
rateable_item

Как грамотно сделать, не имея мн.наследования, например так:

bug extends mdl_item, taggable_item, commentable_item, rateable_item;
product extends mdl_item, rateable_item;

(*все названия вымышлены, ни один программист не пострадал*)
 

whirlwind

TDD infected, paranoid
surg30n завяжи taggable_item, commentable_item, rateable_item на интерфейс mdl_item. Потом когда нужно

PHP:
$bug = new Bug();
$taggableBug = new taggable_item($bug);
 

surg30n

Новичок
whirlwind
Не понял вас.
А где здесь совмещение фукнционала taggable_item, commentable_item, rateable_item для объекта $taggableBug?
 

Santiago

Новичок
Вместо множественного наследования можно использовать паттерн делегирования.

PHP:
<?php
 
class A {
 
        public function f() {
                print "А: Вызываем метод f()<br>";
        }
 
        public function g() {
                print "А: Вызываем метод g()<br>";
        }
}
 
class C {
 
        private $_a;
 
        public function __construct() {
                $this->_a = new A;
        }
 
        public function f() {
                $this->_a->f();
        }
 
        public function g() {
                $this->_a->g();
        }
 
        public function y() {
                print "C: вызываем метод y()<br>";
        }
}
 
$obj = new C;
$obj->f();
$obj->g();
$obj->y();
 
?>
 

whirlwind

TDD infected, paranoid
> А где здесь совмещение фукнционала taggable_item, commentable_item, rateable_item для объекта $taggableBug?

Сначала ответьте на вопрос - а зачем объекту $bug знать о том что он taggable, rateable или commentable?

> принес заметку про вашего мальчика

Допустим, заметка это коммент. Как заметка повлияла непосредственно на мальчика? Никак. Мальчик может и не знать, что про него написали заметку. А вот заметка про мальчика знать обязана, так как она про него.
 

Подрыв мозга

Новичок
triumvirat

>>> какая разница, методы все равно писать под эти интерфейсы. а методы одинаковые.

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

>>> и получится куча мала
2) Не получится, все так делают. Как уже было замечено, первое правило использования наследования: если можешь не не наследовать - не наследуй. В любом случае выбора у вас нет. Только если перейти на С++.

>>> о да это аргумент!
3) Вполне. Если у вас есть класс ГрузовикСПрицепом, то дложен быть класс Грузовик. Теперь если вы решите добавить туда кондиционер, то у вас получтистся ГрузовикСКондиционером. А если вам понадобится ГрузовикСПрицепомСКондиционером от какого класса вы будете наследовать?
 

surg30n

Новичок
whirlwind

Пример
PHP:
class mdl { protected $id; }

class taggable {
	private $tags;
	function __construct() { $this->tags = $this->load_tags(); }
	private function load_tags() { some_lib::load_comments_by_pid($this->id); }
	function get_tags() { return $this->tags; }
}

class commentable {
	private $comments;
	function __construct() { $this->comments = $this->load_comments(); }
	private function load_comments() { some_lib::load_comments_by_pid($this->id); }
	function get_comments() { return $this->comments; }
}

class bug extends mdl, taggable, commentable;

$bug = new mdl();
$bug->get_tags();
$bug->get_comments();
В таком варианте мне было бы удобнее работать, не используя всякие шаблоны делегирования, прокси, обсерверов и других модных нынче словопаттернов.
 

whirlwind

TDD infected, paranoid
surg30n Предположим, что в commentable появился баг. теперь все классы модели, которые вы хотите commentable у Вас не работают. Даже там, когда комментарии нафик не нужны. Впрочем, если у Вас работоспособность автомобиля зависит от журнала в бардачке, или от чехлов на сидениях... никто же Вас не заставляет.
 

surg30n

Новичок
whirlwind
Не убедительно. Таким образом можно высказаться про любую структуру. Опять же можно для каждой из компонент написать тесты, что бы багов не появлялось.. Но в любом случае, в пхп м.наследования нет и приходится писать тону лишнего кода для реализации того же делегирования и пр.

в примере я конечно имеел ввиду $bug = new bug();
 

Подрыв мозга

Новичок
surg30n

Сделайте классы Tagger, Сommenter и передавайте в их конструктор (или методы) экземпляра класса mdl. Не надо помещать все в один класс. Лучше сделать три независимых класса, котрые делают что-то одно, чем один класс универсал-монстр.
 

Духовность™

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

-~{}~ 04.09.08 19:03:

tony2001
да, ты прав.
 

HraKK

Мудак
Команда форума
triumvirat
Давай скооперируйся с Кощей и у вас все получиться!
 

Подрыв мозга

Новичок
Автор оригинала: triumvirat
и какой в этом смысл? объект подразумевает под собой единство методов и их свойств. А Вы предлагаете объект на две и более половинки разрубить.
Угу, это в старых умных книжках так написано, только это единство можно понимать очень по-разному.



Задача
Создать объект Lorry (Грузовик), обладающий свойствами классов Car и Trailer.

Дано
1. PHP
2. Объекты Car (Автомобиль), Trailer (Прицеп).


Решение 1. Классическое, с наследованием аггрегацией и делегированием.

Кому-то нужно создать класс с множественным наследованием. Из принципа, шоб було. Это классическое решение, оно некузявое, но оно работает и все равно другого способа в PHP нет.

PHP:
interface ICar
{
	public function drive();
}

interface ITrailer
{
	public function load();
}	

public class Car implements ICar
{
	public function drive() { }
}

public class Trailer implements ITrailer
{
	public function load() { }
}			

class Lorry extends Car implements ITrialer
{	
	private var $trialer = new Trailer();	

	public function load()
	{
		return $this->trialer->load();
	}	
}
Решение 2. Наследование и агрегация без делегирования.

Но надо ли грузовикуу знать о том, что находиться в прицепе? Зачем ему содержать в себе методы прицепа? А если мы захотм новый прицеп побольше, покрасивше? Тогда менять грузовик целиком? Методы класса должны быть однородны, узкоспециализированы, как говорят, обладать высокой зацепленностью (High cohesion). Поэтому просто делаем прицеп свойством класса Грузовик

PHP:
class Lorry extends Car
{	
	public var $trialer = new Trailer();
}
Решение 3. Когда наследование не нужно.

Может быть так, что классу Lorry не нужно публиковать методы одного или обоих классов предков, а он использует их внутри себя. Тогда, может быть, лучше спрятать их, чем множественное наследование ибо... да, да священная Инкапсуляция.

PHP:
class Lorry
{
	private var $car = new Car();
	private var $trialer = new Trailer();
	
	public function doSomething()
	{
		$this->tralaer->load();
		$this->car->drive();
	}	
}
Решение 4. Аггрегация без наследования и делегирования.

Очень часто (по крайней мере, к этому надо сремиться) свойства Автомобиля и Прицепа не используются в программе одновременно. Например грузчику, который загружает прицеп, совершенно наплевать на то, какому автомобилю он принадлежит, а водителю, в свою очередь, по барабану, что лежит в прицепе. В этом случае наследование вообще не нужно, а Грузовик авляется комбинацией Авотмобиля и Прицепа. Это значает что классы Водитель и Грузчик никак не заисят от неинтересующих их частей грузовика это приводит к низкой сязанности классов (Low coupling).

PHP:
class Lorry
{
	public var $car = new Car();
	public var $trailer = new Trailer();
}	

class Driver
{
	function drive(ICar $car)
	{
		$car->drive();
	}
}

class Loader
{
	function load(ITrailer $trailer)
	{
		$trailer->load();
	}
}

$lorry = new Lorry();
$driver->drive($lorry->car);
$loader->load($lorry->trailer);
Решение 5. Классы-помощники.

Предположим, есть водитель, активно пользующийся грузовиком, его интересует скорость автомобиля, вес и высота. Значит, класс грузовика должен выглядеть так.

PHP:
class Lorry extends Car
{	
	private var $trialer = new Trailer();
	
	public function getSpeed(){ }
	public function getWeight(){ }
	public function getHeight(){ }		
}
Но тут на сцене появляется Бухгалтер и его интересует стоимость автомобиля, мы добавляем в него метод getPrice(), потом повляется Блондинка, и ее интересует количество шашечек, добавляем метод getChequeredPattern(), потом пояляется Физик... Ой ё! В результае, в классе оказывается стописят методов. Это говорит о том, что в зависимости от контекста, объект может быть рассмотрен с разных точек зрения.

На это есть два подхода: минималистический и гуманистический. Гуманисты говорят: нам очень часто нужно получать последнее значение массива, поэтому довайте добавим туда метод last(), а заодно first(), а потом еще немного и еще немного. Минималисты говорят: нефиг засирать интерфейс класса, у вас есть все необходимое, а последний элемент можно получить прямым вычислением вот так.
PHP:
$last = $array[count($array) - 1]
Но есть еще способ: классы-помощники, содержащие в себе статическикие методы, с их использованием вышеприведенный пример выглядит так.
PHP:
ArrayHelper::last($array).
Не очень здорово, правда? Однако это лучше, чем запихивать все-все-все в один класс. Кроме того, например, в С# имеются классы-расширения, статические методы которых можно вызывать как методы экземпляра, поэтому пример
PHP:
ArrayHelper::last($array)
на C# можно переписать так
PHP:
static class ArrayHelper
{
	public static Last(this int[] array) 
	{ 
		return array[array.Length - 1]; 
	} // Обратите внимание на this.
}

array.Last(); // Здесь вызывается метод класса ArrayHelper на самом деле.
Tо, что надо.

Методы расширения - это первый шаг к DSL (Domain Specific Language) - языков конкретных задач. Для Физика это будет один язык, для Блондинки другой. Однако в PHP этого нет. Поэтому наиболее близким к DSL решением, не смешивать все контксты в одном классе, являются классы-помщники.

PHP:
class PhysicLorryHelper
{
	public static getMass($lorry) { }
}

class BlondLorryHelper
{
	public static getChequeredPattern($lorry) { }	
}
Решение 6. Функциональное.

Однако есть еще психи-вероотступники попирающие святую троицу Полиморфизм, Инкапсуляция, Наследование и заявляющие, что, дескать, объекты вообще не должны содеражть методов - этo просто данные, а функции должны существовать сами по себе, хуже того, сами функции тоже должны быть объектами. Да, а еще объекты должны быть неизменяемыми. Этих еретиков зовут функциональщиками. Собственно, мы все немного функциональщики, ведь SQL это функциональный язык.

Допустим, вы решили сохранить объект Lorry в базу данных. Вы продвинуый чувак и используете модную ORM. Да вот беда - класc Lorry содержит в себе огромное текстовое поле, содержащее в себе всю историю его поездок, а пользвателю надо всего лишь показать два поля - грузопдъемность и номер. Проблема в том, что ему не нужен весь объект целиком, а нужна только его часть, иначе это приведет к большим потерям производительности. И тут начинаюся танцы с бубном: в ORM вводятся механизмы кеширования, ленивой загрузки, упреждающего чтения. И все равно это не будет работать оптимально. В конченом итоге, пишется хак в виде прямых SELECTов для ондих случаев и для других. Ради чего все это? чтобы не изменять святым пинципам ООП? Вот пример запроса на С# (частично функциональный язык), выбирающий только два поля из базы.

PHP:
var context = new DatabseContext();	
var query = from lorry in context.Lorry where lorry.Сapacity > 2000 select new { lorry.Capacity, lorry.Numer };

foreach (var lorry in query)
{
	Console.WriteLine(lorry);
}
В сайтах типа Amazon.com 90% функционала - это тупое отображение данных из базы на страницу. Что угодно может быть показано как угодно. Там ООП с его косными классами не то что полезен, а даже вреден, ибо на каждый случай класс не придумешь. В новой версии PHP пытаются ввести основы функционального программирования, так что будем посмотреть.


Решение 7. Кодогенерация.

Справедливости ради надо сказать, что иногда множественное наследоваение все же нужно или просто красиво. Но если его нет, можно изпользовать кодогенерацию. Да это не то, что хотелось бы, но... в конце концов делать это раком тоже надо уметь.
 
Сверху