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

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

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

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

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

Спасибо!

Не так много случаев, когда наследование (или даже множественное наследование) является оправданным. Посмотрите на принципы SOLID. Предпочитайте композицию наследованию.

Ondřej Mirtes 24.05.2012 01:07

@ OndřejMirtes, что вы имеете в виду - «не так много случаев, когда наследование оправдано»?

styler1972 21.10.2012 10:53

Я имею в виду - наследование приносит больше проблем, чем пользы (посмотрите на принцип подстановки Лискова). С композицией можно решить практически все и избавиться от головной боли. Наследование также статично - это означает, что вы не можете изменить то, что уже написано в коде. Но композицию можно использовать во время выполнения, и вы можете динамически выбирать реализации - e. грамм. повторно использовать один и тот же класс с разными механизмами кэширования.

Ondřej Mirtes 21.10.2012 22:11

У PHP 5.4 есть "особенности": stackoverflow.com/a/13966131/492130

f.ardelian 17.06.2013 12:48

@ OndřejMirtes: LSP по своей сути не конфликтует с множественным наследованием. Например, Triangle вполне может быть Finite (с функцией boundingRectangle), а также Intersectable (с функцией intersects), вообще не мешая LSP.

Sebastian Mach 10.02.2016 15:36

Новичкам предлагаю никогда не используйте наследование. В общем, только две ситуации, в которых разрешено наследование: 1) при создании библиотеки, поэтому пользователи пишут меньше кода, и 2) когда руководитель проекта требует, чтобы вы использовали ее.

gurghet 24.08.2016 18:05
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
97
6
99 713
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

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

Лучший ответ ИМО.

l00k 03.10.2018 21:24

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

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

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

Может быть, вы можете заменить отношение «есть-а» на отношение «имеет-а»? Приглашение может содержать сообщение, но не обязательно, чтобы оно было «есть». Приглашение, например. может быть подтверждено, что не очень хорошо сочетается с моделью сообщения.

Поищите «композиция против наследования», если вам нужно узнать об этом больше.

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

Интерфейсы не позволяют реализовывать конкретные функции, поэтому здесь они бесполезны.

Alex Weinstein 22.09.2008 08:13

Интерфейсы действительно поддерживают множественное наследование, в отличие от классов.

Craig Lewis 01.05.2010 01:18
Ответ принят как подходящий

Алекс, в большинстве случаев множественное наследование является сигналом, что структура вашего объекта несколько неверна. В описанной вами ситуации я вижу, что у вас слишком широкая классовая ответственность. Если сообщение является частью бизнес-модели приложения, оно не должно заботиться о рендеринге вывода. Вместо этого вы можете разделить ответственность и использовать 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 в переданном объекте сообщения.

// 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, а алгоритм отправки делегируется диспетчеру. Это называется паттерном стратегии, вы можете прочитать о нем подробнее здесь.

Невероятно обширный ответ, спасибо! Я кое-что узнал сегодня!

Alex Weinstein 22.09.2008 08:22

... Я знаю, что это немного устарело (я искал, есть ли у PHP MI ... просто из любопытства). Я не думаю, что это хороший пример шаблона стратегии. Шаблон стратегии разработан таким образом, чтобы вы могли реализовать новую «стратегию» в любое время. В предоставленной вами реализации такой возможности нет. Вместо этого в сообщении должна быть функция «send», которая вызывает MessageDispatcher-> dispatch () (Dispatcher либо параметр, либо член var), а новые классы HTMLDispatcher и TextDispatcher будут реализовывать «диспетчеризацию» соответствующими способами (это позволяет другим диспетчерам выполнять другая работа)

Terence Honles 02.03.2010 03:55

К сожалению, PHP не подходит для реализации шаблона стратегии. Языки, поддерживающие перегрузку методов, здесь работают лучше - представьте, что у вас есть два метода с одним и тем же именем: отправка (HTMLMessage $ m) и отправка (TextMessage $) - теперь в строго типизированном языке компилятор / интерпретатор будет автоматически использовать правильную «стратегию» на основе тип параметра. Кроме того, я не думаю, что открытость для реализации новой стратегии - это суть паттерна стратегии. Конечно, это хорошо, но часто не является обязательным требованием.

Michał Niedźwiedzki 03.03.2010 04:35

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

Olivier Pons 07.06.2014 19:03

Предположим, у вас есть класс Tracing (это всего лишь образец), в котором вы хотите иметь общие вещи, такие как отладка в файл, отправка SMS для критических проблем и т. д. Все ваши классы - дети этого класса. Теперь предположим, что вы хотите создать класс Exception, который должен иметь эти функции (= дочерний элемент Tracing). Этот класс должен быть дочерним по отношению к Exception. Как спроектировать такие штуки с множественным наследованием без? Да, у вас всегда может быть решение, но вы всегда будете близки к взлому. А взлом = дорогостоящее решение в долгосрочной перспективе. Конец истории.

Olivier Pons 07.06.2014 19:04

Оливье Понс, я не думаю, что создание подклассов Tracing будет правильным решением для вашего варианта использования. Что-то столь же простое, как наличие абстрактного класса трассировки со статическими методами Debug, SendSMS и т. д., Которые затем можно вызывать из любого другого класса с помощью Tracing :: SendSMS () и т. д. Другие ваши классы не являются «типами» трассировки, они «используют» трассировку. Примечание: некоторые люди могут предпочесть синглтон статическим методам; Я предпочитаю использовать статические методы, когда это возможно.

user1358298 23.03.2015 20:20

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

Ejaz 08.07.2018 16:24

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

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

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

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

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

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

Думаю, это возможно. Он объединит функциональность нескольких классов, но не будет наследовать их (в смысле instanceof).

user102008 21.07.2011 09:11

И это наверняка не позволит разрешить переопределения, как только внутренний класс вызовет self :: <whatever>

Phil Lello 07.03.2015 21:38

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

PHP, like Java, does not support multiple inheritance.

Coming in PHP 5.4 will be traits which attempt to provide a solution to this problem.

In the meantime, you would be best to re-think your class design. You can implement multiple interfaces if you're after an extended API to your classes.

И Крис ....

PHP doesn't really support multiple inheritance, but there are some (somewhat messy) ways to implement it. Check out this URL for some examples:

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

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

Черты характера - это то, что нужно

Jonathan 21.12.2018 01:00

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

Итак, иерархия идет:

Сообщение --- Приглашение
------ TextMessage
------ Сообщение электронной почты

А в классе Invitation добавьте функциональность, которая была в InvitationTextMessage и InvitationEmailMessage.

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

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

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

Traits is a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.

Другие вопросы по теме