it-swarm-eu.dev

Eredità multipla in PHP

Sto cercando un modo buono e pulito per aggirare il fatto che PHP5 non supporta ancora l'ereditarietà multipla. Ecco la gerarchia di classi:

Messaggio
-- Messaggio testuale
-------- InvitationTextMessage
-- Messaggio email
-------- InvitationEmailMessage

I due tipi di classi di invito * hanno molto in comune; Mi piacerebbe avere una classe genitore comune, Invitation, che entrambi erediterebbero. Sfortunatamente, hanno anche molto in comune con i loro attuali antenati ... TextMessage e EmailMessage. Il desiderio classico di eredità multipla qui.

Qual è l'approccio più leggero per risolvere il problema?

Grazie!

97
Alex Weinstein

Alex, la maggior parte delle volte che hai bisogno di ereditarietà multipla è un segnale che la tua struttura dell'oggetto è in qualche modo errata. Nella situazione che hai delineato, vedo che hai una responsabilità di classe semplicemente troppo ampia. Se Message fa parte del modello di business dell'applicazione, non dovrebbe occuparsi del rendering dell'output. Invece, è possibile dividere la responsabilità e utilizzare MessageDispatcher che invia il messaggio passato utilizzando testo o back-end HTML. Non conosco il tuo codice, ma fammi simulare in questo modo:

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

In questo modo puoi aggiungere qualche specializzazione alla 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);

Notare che MessageDispatcher prenderebbe una decisione se inviare come HTML o testo semplice a seconda della proprietà type nell'oggetto Message passato.

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

Per riassumere, la responsabilità è suddivisa in due classi. La configurazione del messaggio viene eseguita nella classe InvitationHTMLMessage/InvitationTextMessage e l'invio dell'algoritmo viene delegato al dispatcher. Questo si chiama Strategy Pattern, puoi leggere di più su di esso qui .

142
Michał Rudnicki

Forse puoi sostituire una relazione 'is-a' con una relazione 'has-a'? Un invito potrebbe contenere un messaggio, ma non deve necessariamente essere un messaggio "is-a". Un invito ad es. potrebbe essere confermato, che non va bene insieme al modello di messaggio.

Cerca "composizione vs. eredità" se hai bisogno di saperne di più.

14

Se posso citare Phil in questa discussione ...

PHP, come Java, non supporta l'ereditarietà multipla.

In arrivo PHP 5.4 saranno tratti che tentano di fornire una soluzione a questo problema .

Nel frattempo, saresti meglio ripensare il tuo design di classe. Puoi implementare più interfacce se stai cercando un'API estesa per le tue classi.

E Chris ....

PHP in realtà non supporta l'ereditarietà multipla, ma ci sono alcuni modi (piuttosto disordinati) per implementarla. Dai un'occhiata a questo URL per alcuni esempi:

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

Pensavo che entrambi avessero collegamenti utili. Non vedo l'ora di provare tratti o forse alcuni mixin ...

9
Simon East

Il framework Symfony ha un plug-in mixin per questo , potresti volerlo provare - anche solo per idee, se non per usarlo.

La risposta del "modello di progettazione" consiste nell'astrarre la funzionalità condivisa in un componente separato e comporre in fase di esecuzione. Pensa a un modo per sottrarre la funzionalità Invito come una classe che viene associata alle tue classi di messaggi in un modo diverso dall'eredità.

6
joelhardi

Sto usando tratti in PHP 5.4 come modo di risolverlo. http://php.net/manual/en/language.oop5.traits.php

Ciò consente l'ereditarietà classica con estensioni, ma offre anche la possibilità di inserire funzionalità e proprietà comuni in un 'tratto'. Come dice il manuale:

I tratti sono un meccanismo per il riutilizzo del codice in singoli linguaggi di ereditarietà come PHP. Un tratto ha lo scopo di ridurre alcune limitazioni della singola eredità consentendo a uno sviluppatore di riutilizzare liberamente gruppi di metodi in diverse classi indipendenti che vivono in gerarchie di classi diverse.

4
MatthewPearson

Questa è sia una domanda che una soluzione ....

Che dire dei magici _call (), _ get (), __set () metodi? Non ho ancora testato questa soluzione, ma cosa succede se si crea una classe multiInherit. Una variabile protetta in una classe figlio potrebbe contenere una matrice di classi da ereditare. Il costruttore nella classe multi-interfaccia potrebbe creare istanze di ciascuna delle classi che vengono ereditate e collegarle a una proprietà privata, ad esempio _ext. Il metodo __call () potrebbe utilizzare la funzione method_exists () su ciascuna delle classi nell'array _ext per individuare il metodo corretto da chiamare. __get () e __set potrebbero essere usati per localizzare le proprietà interne, oppure se sei un esperto con riferimenti potresti fare in modo che le proprietà della classe figlio e le classi ereditate siano riferimenti agli stessi dati. L'eredità multipla del tuo oggetto sarebbe trasparente al codice usando quegli oggetti. Inoltre, gli oggetti interni potrebbero accedere direttamente agli oggetti ereditati, se necessario, purché l'array _ext sia indicizzato dal nome della classe. Ho immaginato di creare questa superclasse e non l'ho ancora implementata poiché ritengo che, se funziona, potrebbe portare a sviluppare alcune cattive abitudini di programmazione.

3
Ralph Ritoch

Sembra che motivo decorativo possa essere adatto, ma difficile da dire senza ulteriori dettagli.

3
danio

Ho un paio di domande da porre per chiarire cosa stai facendo:

1) Il tuo oggetto messaggio solo contiene un messaggio ad es. corpo, destinatario, orario? 2) Cosa intendi fare con il tuo oggetto Invito? Deve essere trattato in modo speciale rispetto a un EmailMessage? 3) In tal caso, COSA è così speciale? 4) In tal caso, perché i tipi di messaggio devono essere gestiti in modo diverso per un invito? 5) Cosa succede se si desidera inviare un messaggio di benvenuto o un messaggio OK? Sono anche nuovi oggetti?

Sembra che tu stia provando a combinare troppe funzionalità in un insieme di oggetti che dovrebbero occuparsi solo di contenere il contenuto di un messaggio, e non come dovrebbe essere gestito. Per me, vedi, non c'è differenza tra un invito o un messaggio standard. Se l'invito richiede una gestione speciale, significa che la logica dell'applicazione e non un tipo di messaggio.

Ad esempio: un sistema che ho creato aveva un oggetto messaggio di base condiviso che è stato esteso in SMS, Email e altri tipi di messaggi. Tuttavia: questi non sono stati estesi ulteriormente - un messaggio di invito era semplicemente un testo predefinito da inviare tramite un messaggio di tipo Email. Una specifica richiesta di invito riguarderebbe la convalida e altri requisiti per un invito. Dopotutto, tutto ciò che vuoi fare è inviare il messaggio X al destinatario Y che dovrebbe essere un sistema discreto a sé stante.

1
Dave

Stesso problema di Java. Prova a utilizzare interfacce con funzioni astratte per risolvere quel problema

0
DeeCee

PHP supporta le interfacce. Questa potrebbe essere una buona scommessa, a seconda dei casi d'uso.

0
Cheekysoft