Herencia que salió mal

12

Tengo algún código donde un buen modelo de herencia se ha ido cuesta abajo y estoy tratando de entender por qué y cómo solucionarlo. Básicamente, imagina que tienes una jerarquía de zoológico con:

class Animal  
class Parrot : Animal 
class Elephant : Animal 
class Cow : Animal

etc.

Tienes tus métodos de comer (), correr (), etc. y todo está bien. Entonces, un día alguien aparece y dice: nuestra clase CageBuilder funciona muy bien y usa animal.weight () y animal.height (), excepto por el nuevo Bisonte Africano, que es demasiado fuerte y puede romper el muro, así que voy a agregar una propiedad más para la clase Animal: isAfricanBizon () y la utiliza para elegir el material y solo lo reemplaza para la clase AfricanBizon. La siguiente persona viene y hace algo similar y lo siguiente que sabe es que tiene todas estas propiedades específicas para algún subconjunto de la jerarquía en la clase base.

¿Cuál es una buena manera de mejorar / refactorizar dicho código? Una alternativa aquí sería simplemente usar dynamic_casts para verificar los tipos, pero eso atormenta a las personas que llaman y agrega un montón de if-then-else en todo el lugar. Puede tener interfaces más específicas aquí, pero si todo lo que tiene es la referencia de clase base que tampoco ayuda mucho. ¿Cualquier otra sugerencia? Ejemplos?

¡Gracias!

    
pregunta naumcho 15.07.2011 - 00:03

9 respuestas

13

Parece que el problema es que en lugar de implementar RequiereConcreteWall (), implementaron una llamada de bandera IsAfricanBison (), y luego movieron la lógica sobre si el muro debería cambiar o no fuera del alcance de la clase. Sus clases deben exponer el comportamiento y los requisitos, no la identidad; sus consumidores de estas clases deberían trabajar a partir de lo que se les dice, no en función de lo que son.

    
respondido por el Josh 15.07.2011 - 00:09
12

isAfricanBizon () no es genérico. Supongamos que extiendes tu granja de animales con un hippopotamus que también es demasiado fuerte, pero el hecho de volver real desde isAfricanBizon () para tener el efecto adecuado sería simplemente tonto.

siempre desea agregar métodos a la interfaz que responden a la pregunta específica, en este caso sería algo como la fuerza ()

    
respondido por el Karoly Horvath 15.07.2011 - 00:08
3

Creo que su problema es el siguiente: tiene varios clientes de la biblioteca que están interesados solo en un subconjunto de la jerarquía pero que pasan un puntero / referencia a la clase base. De hecho, ese es el problema que dynamic_cast < > está ahí para resolver.

Es una cuestión de diseño de los clientes minimizar el uso de dynamic_cast < & gt ;; deben usarlo para determinar si el objeto requiere un tratamiento especial y, en caso afirmativo, realizar todas las operaciones en la referencia en minúsculas.

Si tiene colecciones de funciones de tipo "mix-in" que se aplican a varias sub-jerarquías separadas, es posible que desee usar el patrón de interfaz que usan Java y C #; tenga una clase base virtual que sea una clase virtual pura y use dynamic_cast < > para determinar si una instancia le proporciona una implementación.

    
respondido por el antlersoft 15.07.2011 - 00:26
1

Una cosa que puedes hacer es reemplazar la verificación explícita de tipo como isAfricanBison() con la verificación de las propiedades que realmente te interesan, es decir, isTooStrong() .

    
respondido por el hammar 15.07.2011 - 00:10
1

Los animales no deberían preocuparse por las paredes de concreto. Tal vez puedas expresarlo con valores simples.

class Animal {
public:
  virtual ~Animal() {}
  virtual size_t height() const = 0;
  virtual size_t weight() const = 0;
  virtual bool isStrong() const = 0;
};

Cage *CreateCageFromSQL(Animal &a);
Cage *CreateCageFromOrangePeelsAndSticks(Animal &a);

Sin embargo, sospecho que no es viable. Ese es el problema con los ejemplos de juguetes, sin embargo.

Nunca querría ver a RequirConcreteWalls () o líneas y líneas de lanzamientos de punteros dinámicos en cualquier caso.

Esto suele ser una solución barata . Es fácil de mantener y conceptualizar. Y realmente, el problema dice que de todos modos está vinculado al tipo de animal.

class Animal {
public:
  virtual ~Animal() {}
  virtual CageBuilder *getCageBuilder() = 0;
};

Esto tampoco te impide usar código compartido, solo contamina a Animal un poco.

Pero la forma en que se construye la jaula puede ser una política de algún otro sistema, y quizás tenga más de un tipo de constructor de jaulas por animal. Hay muchas combinaciones extrañas y complicadas que puedes encontrar.

He usado Diseño basado en componentes para buenos fines, el principal problema con esto es que puede ser problemático cuando se comparte la propiedad del animal. Cómo evitar tirar en destructores siendo el punto doloroso.

Double Dispatch es otra opción, aunque siempre he sido reticente a saltar a ella.

Más allá de eso, es difícil adivinar el problema.

    
respondido por el Tom Kerr 15.07.2011 - 02:03
0

Bueno, seguramente todos los Animales tienen la propiedad inherente de attemptEscape() . Mientras que algunos el método puede plantear un resultado false en todos los escenarios, mientras que otros pueden tener una posibilidad basada en las heurísticas de sus otras características intrínsecas, como size y weight . Entonces, ciertamente, en algún punto, attemptEscape() se vuelve trivial, ya que sin duda devolverá true .

Me temo que no entiendo completamente tu pregunta, aunque ... todos los animales tienen acciones y características relacionadas. Los específicos del animal deben introducirse donde sea apropiado. Tratar de relacionar directamente Bison con loros no es una buena configuración de herencia y realmente no debería ser un problema en un diseño adecuado.

    
respondido por el TheCapn 15.07.2011 - 00:10
-1

Otra opción sería usar una fábrica que cree jaulas apropiadas para cada animal. Creo que esto puede ser mejor en caso de que las condiciones sean muy diferentes para cada uno de ellos. Pero si es solo esta condición, el método RequiresConcreteWall() mencionado anteriormente lo hará.

    
respondido por el RocketR 15.07.2011 - 00:51
-1

¿qué hay de RecommendCageType () como oppsed a RequiereConcreteWall ()

    
respondido por el Morons 17.07.2011 - 16:33
-2

¿Por qué no hacer algo como esto?

class Animals { /***/ } class HeavyAnimals{} : Animals //The basic class for animals like the African Bison

Con la clase HeavyAnimals puedes crear la clase Bisonte Africano extendiendo la clase HeavyAnimals.

Así que ahora, la clase padre (los Animales) que se puede usar para crear otra clase base como la clase HeavyAnimal se puede usar para crear la clase de Bisontes Africanos y otros Animales Pesados. Así que con el Bisonte africano ahora tienes acceso a los métodos y propiedades de la clase Animal (esta es la base para todos los animales) y al acceso a la clase HeavyAnimals (esta es la base para animales pesados)

    
respondido por el mohhamed rafi 17.07.2015 - 19:49

Lea otras preguntas en las etiquetas