2008-08-15 11 views
5

En un proyecto, nuestro equipo está utilizando listas de objetos para realizar operaciones masivas en conjuntos de datos que deberían procesarse de forma similar. En particular, diferentes objetos idealmente actuarían de la misma manera, lo que se lograría muy fácilmente con polimorfismo. El problema que tengo con él es que la herencia implica que es una relación, en lugar de tiene una relación. Por ejemplo, varios objetos tienen un contador de daños, pero para hacer que esto sea fácil de usar en una lista de objetos, se podría usar polimorfismo, excepto que implicaría que es una relación que no sería cierta. (Una persona no es un contador de daños)Herencia y polimorfismo: facilidad de uso vs pureza

La única solución que puedo pensar es hacer que un miembro de la clase devuelva el tipo de objeto adecuado cuando se establece implícitamente en lugar de confiar en la herencia. ¿Sería mejor renunciar a la es una/tiene un ideal a cambio de la facilidad de programación? Edit: Para ser más específicos, estoy usando C++, por lo que usar polimorfismo permitiría que los diferentes objetos "actúen igual" en el sentido de que las clases derivadas podrían residir dentro de una sola lista y ser operadas por un función virtual de la clase base. El uso de una interfaz (o imitarlos a través de la herencia) parece una solución que estaría dispuesto a usar.

Respuesta

3

Esto se puede lograr utilizando herencia múltiple. En su caso específico (C++), puede usar clases virtuales puras como interfaces. Esto le permite tener herencia múltiple sin crear problemas de alcance/ambigüedad. Ejemplo:

class Damage { 
    virtual void addDamage(int d) = 0; 
    virtual int getDamage() = 0; 
}; 

class Person : public virtual Damage { 
    void addDamage(int d) { 
     // ... 
     damage += d * 2; 
    } 

    int getDamage() { 
     return damage; 
    } 
}; 

class Car : public virtual Damage { 
    void addDamage(int d) { 
     // ... 
     damage += d; 
    } 

    int getDamage() { 
     return damage; 
    } 
}; 

Ahora tanto la persona como coche 'es-un' daño, es decir, que implementan la interfaz daños. El uso de clases virtuales puras (para que sean como interfaces) es clave y debe usarse con frecuencia. Aísla los cambios futuros de la alteración de todo el sistema. Lea sobre el principio abierto-cerrado para más información.

0

A veces vale la pena perder el ideal para el realista. Si va a causar un problema masivo para "hacerlo bien" sin ningún beneficio real, entonces lo haría mal. Dicho esto, a menudo creo que vale la pena tomarse el tiempo para hacerlo bien, porque la herencia múltiple innecesaria aumenta la complejidad, y puede contribuir a que el sistema sea menos sostenible. Realmente tiene que decidir qué es lo mejor para su circunstancia.

Una opción sería que estos objetos implementen una interfaz Damageable, en lugar de heredar desde DamageCounter. De esta forma, una persona tiene un contador de daños, pero es dañado. (A menudo encuentro que las interfaces tienen mucho más sentido como adjetivo que los sustantivos). Entonces podría tener una interfaz de daños consistente en los objetos Damageable, y no exponer que un contador de daños es la implementación subyacente (a menos que sea necesario).

Si desea ir a la ruta de la plantilla (suponiendo C++ o similar), podría hacer esto con mixins, pero eso puede ponerse muy rápido si se hace mal.

0

Normalmente cuando hablamos de 'es un' vs 'tiene un' estamos hablando de Herencia vs Composición.

Um ... contador de daños simplemente sería atributo de una de sus clases derivadas y realmente no sería discutido en términos de 'Una persona es un contador de daños' con respecto a su pregunta.

ver esto:

http://www.artima.com/designtechniques/compoinh.html

que podría ayudarle a lo largo del camino.

@Derek: A partir de la redacción, que supuso que era una clase base, teniendo volver a leer la pregunta que ahora veo un poco lo que está recibiendo en.

0

Esta pregunta es muy confuso:/

Su pregunta en negrita es muy abierto y tiene una respuesta de "depende", pero su ejemplo realmente no da mucha información sobre el contexto en el que se están preguntando. Estas líneas me confunden;

conjuntos de datos que todos deben ser procesados ​​de una manera similar

qué manera? ¿Los conjuntos son procesados ​​por una función? ¿Otra clase? A través de una función virtual en los datos?

En particular, los diferentes objetos idealmente actuar de la misma, que se logra muy fácilmente con polimorfismo

El ideal de "actuando de la misma" y el polimorfismo son absolutamente no relacionado. ¿Cómo el polimorfismo hace que sea fácil de lograr?

0

@ Kevin

Normalmente cuando hablamos de 'es un' vs 'tiene un' estamos hablando de Herencia frente a la composición.

Um ... contador de daños simplemente sería atributo de una de sus clases derivadas y realmente no sería discutido en términos de 'Una persona es un contador de daños' con respecto a su pregunta.

Tener el contador de daños como atributo no le permite diversificar objetos con contadores de daños en una colección. Por ejemplo, una persona y un automóvil pueden tener contadores de daños, pero no puede tener un vector<Person|Car> o un vector<with::getDamage()> o algo similar en la mayoría de los idiomas. Si tiene una clase base de objeto común, puede empujarlos de esa manera, pero luego no puede acceder al método getDamage() genéricamente.

Esa fue la esencia de su pregunta, mientras la leía. "¿Debo violar is-a y has-a por el bien de tratar ciertos objetos como si fueran iguales, aunque no lo sean?"

0

"Hacerlo bien" tendrá beneficios a largo plazo, aunque solo sea porque a alguien que mantenga el sistema más tarde le resultará más fácil comprender si se hizo correctamente desde el principio.

Según el idioma, es posible que tenga la opción de herencia múltiple, pero las interfaces normalmente simples tienen más sentido. Por "simple" me refiero a hacer una interfaz que no intente ser demasiado. Es mejor tener muchas interfaces simples y algunas monolíticas. Por supuesto, hay siempre una solución de compromiso, y también muchas interfaces probablemente conduciría a que están siendo "olvidados" de ...

0

@ Andrew

El ideal de "actuando de la misma" y el polimorfismo son absolutamente sin relación. ¿Cómo el polimorfismo hace que sea fácil de lograr?

Todos ellos tienen, por ejemplo,, una función en común. Vamos a llamarlo addDamage(). Si quieres hacer algo como esto:

foreach (obj in mylist) 
    obj.addDamage(1) 

entonces usted necesita ya sea un lenguaje dinámico, o que ellos necesitan para extenderse desde una clase padre común (o interfaz). ej .:

class Person : DamageCounter {} 
class Car : DamageCounter {} 

foreach (DamageCounter d in mylist) 
    d.addDamage(1) 

A continuación, se puede tratar Person y Car la misma en ciertas circunstancias muy útiles.

5

que consideran que debería implementación de interfaces para poder cumplir su tiene una relaciones (estoy haciendo esto en C#):

public interface IDamageable 
{ 
    void AddDamage(int i); 
    int DamageCount {get;} 
} 

Se podría implementar esto en sus objetos:

public class Person : IDamageable 

public class House : IDamageable 

Y estarás seguro de que la propiedad DamageCount tiene un método que te permite agregar daño, sin implicar que una persona y una casa estén relacionadas entre sí en algún tipo de jerarquía.

1

Estoy de acuerdo con Jon, pero suponiendo que todavía tengo necesidad para una clase de contador de daño separada, que puede hacer:

class IDamageable { 
    virtual DamageCounter* damage_counter() = 0; 
}; 
class DamageCounter { 
    ... 
}; 

Cada clase damageable entonces tiene que proporcionar su propia función miembro damage_counter(). La desventaja de esto es que crea un vtable para cada clase dañada. en su lugar puede utilizar:

class Damageable { 
public: 
    DamageCounter damage_counter() { return damage_counter_; } 
private: 
    DamageCounter damage_counter_; 
}; 

Sin embargo, muchas personas son no se refresca con herencia múltiple cuando los padres tienen múltiples variables miembro.

0

El polimorfismo no requiere herencia. El polimorfismo es lo que obtienes cuando varios objetos implementan la misma firma de mensaje (método).