Множественное наследование в PHP

Я ищу хороший, чистый способ обойти тот факт, что PHP5 по-прежнему не поддерживает множественное наследование. Здесь иерархия классов:


Сообщение - TextMessage
  -------- InvitationTextMessage
- EmailMessage
  -------- InvitationEmailMessage

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

Какой самый легкий подход для решения проблемы?

Спасибо!

Ответ 1

Алекс, в большинстве случаев вам нужно многократное наследование - это сигнал, что ваша структура объекта несколько некорректна. В ситуации, которую вы изложили, я вижу, что у вас классовая ответственность слишком широка. Если Message является частью бизнес-модели приложения, он не должен заботиться об рендеринге вывода. Вместо этого вы можете разделить ответственность и использовать MessageDispatcher, который отправляет сообщение, переданное с использованием текстового или html файла. Я не знаю вашего кода, но позвольте мне смоделировать его следующим образом:

$m = new Message();
$m->type = 'text/html';
$m->from = 'John Doe <[email protected]>';
$m->to = 'Random Hacker <[email protected]>';
$m->subject = 'Invitation email';
$m->importBody('invitation.html');

$d = new MessageDispatcher();
$d->dispatch($m);

Таким образом, вы можете добавить специализацию в класс Message:

$htmlIM = new InvitationHTMLMessage(); // html type, subject and body configuration in constructor
$textIM = new InvitationTextMessage(); // text type, subject and body configuration in constructor

$d = new MessageDispatcher();
$d->dispatch($htmlIM);
$d->dispatch($textIM);

Обратите внимание, что MessageDispatcher принимает решение о том, отправлять ли как HTML или обычный текст в зависимости от свойства type в переданном объекте Message.

// in MessageDispatcher class
public function dispatch(Message $m) {
    if ($m->type == 'text/plain') {
        $this->sendAsText($m);
    } elseif ($m->type == 'text/html') {
        $this->sendAsHTML($m);
    } else {
        throw new Exception("MIME type {$m->type} not supported");
    }
}

Подводя итог, ответственность разделяется между двумя классами. Конфигурация сообщений выполняется в классе InvitationHTMLMessage/InvitationTextMessage, а алгоритм отправки делегируется диспетчеру. Это называется Strategy Pattern, вы можете прочитать подробнее здесь.

Ответ 2

Возможно, вы можете заменить отношение "is-a" с отношением "has-a"? Приглашение может иметь сообщение, но оно необязательно должно быть сообщено "is-a". Приглашение f.e. может быть подтверждено, что не очень хорошо сочетается с моделью сообщений.

Найдите "композицию против наследования", если вам нужно больше узнать об этом.

Ответ 3

Если я могу указать Фила в этот поток...

PHP, как и Java, не поддерживает множественное наследование.

В PHP 5.4 будет черты, которые пытаются предоставить решение к этой проблеме.

Тем временем вы бы лучше подумали о своем классе. Вы может реализовать несколько интерфейсов, если после расширенного API ваши классы.

И Крис....

PHP на самом деле не поддерживает множественное наследование, но есть некоторые (несколько грязных) способов его реализации. Проверьте этот URL для некоторых примеры:

http://www.jasny.net/articles/how-i-php-multiple-inheritance/

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

Ответ 4

Платформа Symfony имеет для этого плагин mixin, вы можете попробовать его - даже просто для идей, если не использовать его.

Ответ "шаблон проектирования" состоит в том, чтобы разделить общую функциональность на отдельный компонент и создавать во время выполнения. Подумайте о способе абстрагирования функциональности Invitation в виде класса, который каким-либо образом связан с вашими классами Message, кроме наследования.

Ответ 5

Я использую черты в PHP 5.4 как способ решения этого. http://php.net/manual/en/language.oop5.traits.php

Это позволяет классическое наследование с расширением, но также дает возможность помещать общие функции и свойства в "черту". Как говорится в руководстве:

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

Ответ 6

Похоже, что шаблон декоратора может быть подходящим, но трудно сказать без каких-либо подробностей.

Ответ 7

Это и вопрос, и решение.

Как насчет магических методов _call(), _ get(), __set()? Я еще не тестировал это решение, но что делать, если вы создаете класс multiInherit. Защищенная переменная в дочернем классе может содержать массив классов для наследования. Конструктор в классе multi-interface может создавать экземпляры каждого из наследуемых классов и связывать их с частной собственностью, скажем _ext. Метод __call() может использовать функцию method_exists() для каждого из классов массива _ext для поиска правильного метода для вызова. __get() и __set можно использовать для определения внутренних свойств, или если ваш эксперт со ссылками, вы можете сделать свойства дочернего класса и унаследованных классов ссылками на одни и те же данные. Множественное наследование вашего объекта будет прозрачным для кода с использованием этих объектов. Кроме того, внутренние объекты могут обращаться к унаследованным объектам напрямую, если это необходимо, если массив _ext индексируется по имени класса. Я предвидел создание этого суперкласса и еще не реализовал его, поскольку я чувствую, что, если он работает, это может привести к некоторым изменениям плохих привычек программирования.

Ответ 8

У меня есть несколько вопросов, чтобы просить уточнить, что вы делаете:

1) Имеет ли ваш объект сообщения только сообщение, например. тело, получатель, время расписания? 2) Что вы намерены делать со своим объектом Invitation? Нужно ли его обрабатывать специально по сравнению с EmailMessage? 3) Если так ЧТО это так особенное? 4) Если это так, почему типы сообщений нуждаются в обработке по-разному для приглашения? 5) Что делать, если вы хотите отправить приветственное сообщение или сообщение OK? Это тоже новые объекты?

Звучит так, будто вы пытаетесь объединить слишком много функций в набор объектов, которые должны быть связаны только с содержанием содержимого сообщения, а не с тем, как его следует обрабатывать. Для меня, видите ли, нет разницы между приглашением или стандартным сообщением. Если приглашение требует специальной обработки, то это означает логику приложения, а не тип сообщения.

Например: у системы, которую я построил, был общий объект сообщения, который был расширен в SMS, электронную почту и другие типы сообщений. Однако: они не были расширены дальше - приглашение было просто предопределенным текстом, который должен быть отправлен через сообщение типа Email. Специальное приглашение будет касаться проверки и других требований для приглашения. В конце концов, все, что вы хотите сделать, это отправить сообщение X получателю Y, который должен быть дискретной системой в своем собственном праве.

Ответ 9

Такая же проблема, как Java. Попробуйте использовать интерфейсы с абстрактными функциями для решения этой проблемы.

Ответ 10

PHP поддерживает интерфейсы. Это может быть хорошей ставкой, в зависимости от ваших случаев использования.

Ответ 11

Как насчет класса приглашения прямо под классом Message?

поэтому иерархия идет:


Сообщение --- Приглашение
------ TextMessage
------ EmailMessage

И в классе приглашения добавьте функциональность, которая была в InvitationTextMessage и InvitationEmailMessage.

Я знаю, что приглашение на самом деле не является типом сообщения, это скорее функциональность Message. Поэтому я не уверен, что это хороший дизайн OO или нет.