2008-09-18 39 views
90

Estoy buscando una forma buena y limpia de evitar el hecho de que PHP5 todavía no admite herencia múltiple. Aquí está la jerarquía de clases:Herencia múltiple en PHP

mensaje
- TextMessage
-------- InvitationTextMessage
- EmailMessage
-------- InvitationEmailMessage

Los dos tipos de Las clases de invitación * tienen mucho en común; Me encantaría tener una clase común para padres, Invitación, de la que ambos heredarían. Desafortunadamente, también tienen mucho en común con sus antepasados ​​actuales ... TextMessage y EmailMessage. Deseo clásico de herencia múltiple aquí.

¿Cuál es el enfoque más liviano para resolver el problema?

Gracias!

+4

No hay muchos casos en los que la herencia (o incluso la herencia múltiple) sea justificable. Mire los principios SOLIDOS. Prefiere la composición sobre la herencia. –

+2

@ OndřejMirtes ¿a qué te refieres con "pocos casos en los que la herencia es justificable"? – styler1972

+9

Es decir, la herencia trae más problemas que beneficios (observe el principio de sustitución de Liskov). Puedes resolver casi todo con la composición y ahorrar muchos dolores de cabeza. La herencia también es estática; eso significa que no puede cambiar lo que ya está escrito en el código. Pero la composición se puede usar en tiempo de ejecución y puede elegir implementaciones dinámicamente - e. gramo. reutiliza la misma clase con diferentes mecanismos de almacenamiento en caché. –

Respuesta

139

Alex, la mayoría de las veces que necesita herencia múltiple es una señal de que la estructura de su objeto es algo incorrecta. En la situación que describiste, veo que tienes una responsabilidad de clase simplemente demasiado amplia. Si Message forma parte del modelo comercial de la aplicación, no debería preocuparse por generar resultados. En su lugar, puede dividir la responsabilidad y usar MessageDispatcher que envía el mensaje pasado usando texto o back-end html. No sé su código, pero déjame simular de esta manera:

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

esta manera se puede añadir un poco de especialización a la clase de mensaje:

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

Tenga en cuenta que MessageDispatcher tomaría una decisión si se debe enviar como HTML o texto sin formato dependiendo de la propiedad type en el objeto Message pasado.

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

Para resumir, la responsabilidad se divide entre dos clases. La configuración del mensaje se realiza en la clase InvitationHTMLMessage/InvitationTextMessage, y el algoritmo de envío se delega al despachador.Esto se llama Patrón de estrategia, puedes leer más sobre él here.

+12

Sorprendentemente extensa respuesta, ¡gracias! ¡Aprendí algo hoy! –

+22

...Sé que esto es un poco viejo (estaba buscando para ver si PHP tenía MI ... solo por curiosidad) No creo que este sea un buen ejemplo del Patrón de Estrategia. El patrón de estrategia está diseñado para que pueda implementar una nueva "estrategia" en cualquier momento. La implementación que ha proporcionado no tiene esa capacidad. En su lugar, Message debe tener la función "enviar" que llama a MessageDispatcher-> dispatch() (Dispatcher una var param o miembro), y las nuevas clases HTMLDispatcher & TextDispatcher implementará "dispatch" en sus respectivas formas (esto permite a otros despachadores) para hacer otro trabajo) –

+5

Realmente me gustaría que SO no se meta con mis espacios en blanco ... –

6

El marco de Symfony tiene un mixin plugin for this, es posible que desee comprobarlo, incluso para ideas, si no para usarlo.

La respuesta del "patrón de diseño" es abstraer la funcionalidad compartida en un componente separado y componer en tiempo de ejecución. Piense en una forma de abstraer la funcionalidad de Invitación como una clase que se asocia con sus clases de Mensaje de una forma distinta a la herencia.

+0

el enlace de Symfony está roto ... – m13r

0

El mismo problema que Java. Intente usar interfaces con funciones abstractas para resolver ese problema

13

¿Quizás pueda reemplazar una relación 'is-a' con una relación 'has-a'? Una invitación puede tener un mensaje, pero no necesariamente es un mensaje 'es-a'. Una invitación f.e. podría confirmarse, lo que no funciona bien con el modelo de mensaje.

Busque 'composición vs. herencia' si necesita saber más sobre eso.

0

PHP no admite interfaces. Esta podría ser una buena apuesta, dependiendo de sus casos de uso.

+4

Las interfaces no permitir implementaciones de funciones concretas, por lo que no son útiles aquí. –

+0

Las interfaces admiten la herencia múltiple, a diferencia de las clases. –

1

Tengo un par de preguntas para aclarar lo que está haciendo:

1) ¿Su objeto de mensaje solo contienen un mensaje, por ejemplo, cuerpo, destinatario, horario de tiempo? 2) ¿Qué piensas hacer con tu objeto Invitación? ¿Necesita ser tratado especialmente en comparación con un mensaje de correo electrónico? 3) Si es así ¿QUÉ es tan especial al respecto? 4) Si ese es el caso, ¿por qué los tipos de mensaje necesitan un manejo diferente para una invitación? 5) ¿Qué sucede si desea enviar un mensaje de bienvenida o un mensaje de OK? ¿Son nuevos objetos también?

Parece que está intentando combinar demasiada funcionalidad en un conjunto de objetos que solo deberían preocuparse por mantener contenidos de un mensaje, y no cómo deberían manejarse. Para mí, como ves, no hay diferencia entre una invitación o un mensaje estándar. Si la invitación requiere un manejo especial, eso significa lógica de la aplicación y no un tipo de mensaje.

Por ejemplo: un sistema que construí tenía un objeto de mensaje base compartido que se extendió a SMS, correo electrónico y otros tipos de mensajes. Sin embargo, estos no se ampliaron más: un mensaje de invitación era simplemente texto predefinido que se enviará a través de un mensaje de tipo Correo electrónico. Una aplicación de invitación específica estaría relacionada con la validación y otros requisitos para una invitación. Después de todo, todo lo que desea hacer es enviar el mensaje X al destinatario Y, que debería ser un sistema discreto por derecho propio.

3

Esto es tanto una pregunta y una solución ....

¿Qué pasa con la mágica _ llamada(), _GET(),() __set métodos? Todavía no he probado esta solución, pero ¿y si haces una clase multiInherit? Una variable protegida en una clase secundaria podría contener una matriz de clases para heredar. El constructor de la clase de interfaz múltiple podría crear instancias de cada una de las clases que se heredan y vincularlas a una propiedad privada, por ejemplo _ext. El método __call() podría usar la función method_exists() en cada una de las clases en la matriz _ext para localizar el método correcto para llamar. __get() y __set podrían usarse para localizar propiedades internas, o si su experto con referencias podría hacer que las propiedades de la clase hija y las clases heredadas sean referencias a los mismos datos. La herencia múltiple de su objeto sería transparente para codificar usando esos objetos. Además, los objetos internos podrían acceder directamente a los objetos heredados si fuera necesario, siempre que la matriz _ext esté indexada por nombre de clase. He imaginado crear esta superclase y aún no la he implementado, ya que creo que, si funciona, podría generar algunos hábitos de programación diferentes.

+0

Creo que esto es factible. Combinará la funcionalidad de múltiples clases, pero en realidad no las heredará (en el sentido de 'instanceof') – user102008

+0

Y esto seguramente no permitirá las sustituciones tan pronto como en la clase interna haga una llamada a self ::

7

Si puedo citar a Phil en this thread ...

PHP, como Java, no soporta la herencia múltiple.

Viniendo en PHP 5.4 será traits que intentan proporcionar una solución a este problema.

Mientras tanto, sería mejor volver a pensar en el diseño de su clase. Usted puede implementar múltiples interfaces si está buscando una API extendida para sus clases.

Y Chris ....

PHP no es realmente compatible con la herencia múltiple, pero hay algunas maneras de implementarlo ( ) (algo complicadas). Echa un vistazo a este URL para algunos ejemplos:

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

pensamiento que ambos tenían enlaces útiles. No puedo esperar para probar los rasgos o quizás algunos mixins ...

-1

¿Qué tal una clase de invitación justo debajo de la clase de mensaje?

por lo que la jerarquía va:

mensaje
--- Invitación
------ ------ TextMessage
EmailMessage

Y en la clase de la invitación, añadir la funcionalidad eso estaba en InvitationTextMessage e InvitationEmailMessage.

Sé que Invitación no es realmente un tipo de Mensaje, es más una funcionalidad de Mensaje. Entonces no estoy seguro de si este es un buen diseño OO o no.

3

Estoy usando rasgos en PHP 5.4 como la forma de resolver esto. http://php.net/manual/en/language.oop5.traits.php

Esto permite la herencia clásica con extends, pero también da la posibilidad de colocar funcionalidades y propiedades comunes en un 'rasgo'. Como dice el manual:

Rasgos es un mecanismo para la reutilización de códigos en lenguajes de herencia únicos como PHP. Un Rasgo tiene la intención de reducir algunas limitaciones de la herencia individual al permitir que un desarrollador reutilice conjuntos de métodos libremente en varias clases independientes que viven en diferentes jerarquías de clases.