web-dev-qa-db-fra.com

Héritage multiple dans PHP

Je cherche un moyen propre et efficace de contourner le fait que PHP5 ne prend toujours pas en charge l'héritage multiple. Voici la hiérarchie de classe:

Message
-- Message texte
-------- InvitationTextMessage
-- Message électronique
-------- InvitationEmailMessage

Les deux types de classes Invitation * ont beaucoup en commun; J'adorerais avoir une classe parent commune, Invitation, dont ils hériteraient tous les deux. Malheureusement, ils ont également beaucoup en commun avec leurs ancêtres actuels ... TextMessage et EmailMessage. Désir classique pour l'héritage multiple ici.

Quelle est l'approche la plus légère pour résoudre le problème?

Merci!

97
Alex Weinstein

Alex, la plupart du temps, vous avez besoin d'un héritage multiple, c'est un signe que votre structure d'objet est quelque peu incorrecte. Dans la situation que vous avez décrite, je vois que votre responsabilité de classe est trop large. Si Message fait partie du modèle commercial de l'application, il ne doit pas s'occuper du rendu de la sortie. Au lieu de cela, vous pouvez diviser les responsabilités et utiliser MessageDispatcher qui envoie le message passé en utilisant un backend texte ou html. Je ne connais pas votre code, mais laissez-moi le simuler comme suit:

$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);

De cette façon, vous pouvez ajouter une spécialisation à la classe 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);

Notez que MessageDispatcher prendrait la décision d’envoyer au format HTML ou texte brut en fonction de la propriété type dans l’objet Message transmis.

// 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");
    }
}

En résumé, la responsabilité est partagée entre deux classes. La configuration du message est effectuée dans la classe InvitationHTMLMessage/InvitationTextMessage et l'algorithme d'envoi est délégué au répartiteur. Ceci est appelé modèle de stratégie, vous pouvez en lire plus ici .

142
Michał Rudnicki

Peut-être pourriez-vous remplacer une relation "est-un" par une relation "a-un"? Une invitation peut avoir un message, mais il n'est pas nécessaire qu'elle soit un message. Une invitation par exemple peut être confirmé, ce qui ne va pas bien avec le modèle Message.

Recherchez "composition vs héritage" si vous souhaitez en savoir plus à ce sujet.

14
Matthias Kestenholz

Si je peux citer Phil dans ce fil ...

PHP, comme Java, ne supporte pas l'héritage multiple.

À venir PHP 5.4 sera traits qui tente de fournir une solution à ce problème.

En attendant, vous feriez mieux de repenser la conception de votre classe. Vous pouvez implémenter plusieurs interfaces si vous recherchez une API étendue à vos classes.

Et Chris ....

PHP ne supporte pas vraiment l'héritage multiple, mais il existe quelques manières (quelque peu désordonnées) de l'implémenter. Découvrez cette URL pour quelques exemples:

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

Pensé qu'ils avaient tous deux des liens utiles. J'ai hâte d'essayer des traits ou peut-être des mixins ...

9
Simon East

Le framework Symfony a un plugin de mixin pour cela , vous voudrez peut-être le vérifier - même pour des idées, sinon pour l'utiliser.

La réponse "modèle de conception" consiste à résumer la fonctionnalité partagée dans un composant séparé et à composer au moment de l'exécution. Pensez à un moyen de résumer la fonctionnalité d'invitation en tant que classe associée à vos classes Message d'une manière autre que l'héritage.

6
joelhardi

J'utilise des traits dans PHP 5.4 pour résoudre ce problème. http://php.net/manual/en/language.oop5.traits.php

Cela permet un héritage classique avec étend, mais permet également de placer des fonctionnalités et propriétés communes dans un "trait". Comme le dit le manuel:

Traits est un mécanisme de réutilisation de code dans des langages à héritage unique tels que PHP. Un trait est destiné à réduire certaines limitations de l'héritage simple en permettant à un développeur de réutiliser librement des ensembles de méthodes dans plusieurs classes indépendantes vivant dans des hiérarchies de classes différentes.

4
MatthewPearson

C'est à la fois une question et une solution ....

Qu'en est-il des méthodes magiques _call (), _ get (), __set ()? Je n'ai pas encore testé cette solution, mais que se passe-t-il si vous créez une classe multi-héréditaire? Une variable protégée dans une classe enfant peut contenir un tableau de classes à hériter. Le constructeur de la classe multi-interfaces peut créer des instances de chacune des classes héritées et les lier à une propriété privée, par exemple _ext. La méthode __call () peut utiliser la fonction method_exists () sur chacune des classes du tableau _ext pour localiser la méthode correcte à appeler. __get () et __set peuvent être utilisés pour localiser les propriétés internes. Si vous êtes un expert avec des références, vous pouvez définir les propriétés de la classe enfant et des classes héritées avec les mêmes données. L'héritage multiple de votre objet serait transparent pour le code utilisant ces objets. De plus, les objets internes peuvent accéder directement aux objets hérités si nécessaire, à condition que le tableau _ext soit indexé par nom de classe. J'ai envisagé de créer cette super-classe et je ne l'ai pas encore mise en œuvre, car je pense que si cela fonctionne, cela pourrait conduire à la création de mauvaises habitudes de programmation.

3
Ralph Ritoch

Cela ressemble au motif décoratif peut être approprié, mais difficile à dire sans plus de détails.

3
danio

J'ai quelques questions à poser pour clarifier ce que vous faites:

1) Votre objet de message juste contient-il un message, par exemple? corps, destinataire, heure prévue? 2) Qu'avez-vous l'intention de faire avec votre objet Invitation? Doit-il être traité spécialement par rapport à EmailMessage? 3) Si c'est le cas QU'Y A-T-IL de si spécial? 4) Si tel est le cas, pourquoi les types de message doivent-ils être gérés différemment pour une invitation? 5) Que faire si vous souhaitez envoyer un message de bienvenue ou un message OK? Sont-ils de nouveaux objets aussi?

On dirait que vous essayez de combiner trop de fonctionnalités dans un ensemble d’objets qui ne devraient concerner que la conservation du contenu du message - et non la façon dont il devrait être traité. Pour moi, vous voyez, il n'y a pas de différence entre une invitation et un message standard. Si l'invitation nécessite un traitement spécial, il s'agit alors d'une logique d'application et non d'un type de message.

Par exemple: un système que j'ai construit avait un objet de message de base partagé qui a été étendu à SMS, Email et autres types de message. Cependant, ils n'étaient pas étendus: un message d'invitation consistait simplement en un texte prédéfini à envoyer via un message de type Email. Une application d'invitation spécifique concernerait la validation et d'autres conditions requises pour une invitation. Après tout, tout ce que vous voulez faire est d’envoyer le message X au destinataire Y, qui devrait être un système distinct à part entière.

1
Dave

Même problème que Java. Essayez d’utiliser des interfaces avec des fonctions abstraites pour résoudre ce problème.

0
DeeCee

PHP supporte les interfaces. Cela peut être un bon pari, selon vos cas d'utilisation.

0
Cheekysoft