Tengo un código donde un buen modelo de herencia ha ido cuesta abajo y estoy tratando de entender por qué y cómo solucionarlo. Básicamente, imagine que tiene una jerarquía de zoológico con:
class Animal
class Parrot : Animal
class Elephant : Animal
class Cow : Animal
etc.
Tienes tus métodos eat (), run (), etc. y todo está bien. Entonces, un día aparece alguien y dice: nuestra clase CageBuilder funciona muy bien y usa animal.weight () y animal.height (), excepto el nuevo Bisonte africano que es demasiado fuerte y puede romper la pared, así que voy a agregar una propiedad más para la clase Animal: esAfricanBizon () y úsela al elegir el material y solo lo anule para la clase AfricanBizon. La siguiente persona viene y hace algo similar y lo siguiente que sabes es que tienes 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 usar dynamic_casts para verificar los tipos, pero eso abarrota a las personas que llaman y agrega un montón de if-then-else por todas partes. 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!
fuente
Respuestas:
Parece que el problema es que en lugar de implementar RequiereConcretoMural (), 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 comportamiento y requisitos, no identidad; Sus consumidores de estas clases deben trabajar según lo que se les dice, no según lo que son.
fuente
isAfricanBizon () no es genérico. Suponga que extiende su granja de animales con un hipopótamo que también es demasiado fuerte, pero volver a ser cierto de isAfricanBizon () para tener el efecto adecuado sería una tontería.
siempre desea agregar métodos a la interfaz que respondan a la pregunta específica, en este caso sería algo así como fuerza ()
fuente
strength
puede consultar un métodomaterial.canHold(animal)
, permitiendo una forma limpia de soportar diferentes tipos de material queConcreteWall
.Creo que su problema es este: tiene varios clientes de la biblioteca que están interesados solo en un subconjunto de la jerarquía pero a los que se les pasa 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 <>; deben usarlo para determinar si el objeto requiere un tratamiento especial y, en caso afirmativo, hacer todas las operaciones en la referencia de fundición descendente.
Si tiene colecciones de funcionalidades 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 #; tener una clase base virtual que es una clase puramente virtual, y usar dynamic_cast <> para determinar si una instancia proporciona una implementación para ella.
fuente
Una cosa que puede hacer es reemplazar la comprobación explícita de tipo, como la
isAfricanBison()
comprobación de las propiedades que realmente le interesan, es decirisTooStrong()
.fuente
Los animales no deberían preocuparse por los muros de hormigón. Quizás puedas expresarlo con valores simples.
Aunque sospecho que eso no es viable. Sin embargo, ese es el problema con los ejemplos de juguetes.
Nunca quisiera ver RequiereConcreteWalls () o líneas y líneas de conversiones de puntero dinámico en cualquier caso.
Esta suele ser una solución barata . Es fácil de mantener y conceptualizar. Y realmente, el problema indica que está vinculado al tipo de animal de todos modos.
Esto tampoco le impide usar código compartido, solo contamina un poco a Animal.
Pero la forma en que se construye la jaula puede ser una política de algún otro sistema, y tal vez tenga más de un tipo de constructor de jaulas por animal. Hay muchas combinaciones extrañas y complicadas que puedes encontrar.
He utilizado el diseño basado en componentes para buenos fines, el principal problema es que puede ser problemático cuando se comparte la propiedad de Animal. Cómo evitar tirar destructores siendo el punto de dolor.
Double Dispatch es otra opción, aunque siempre he sido reticente a participar.
Más allá de eso, es difícil adivinar el problema.
fuente
Bueno, seguramente todos los animales tienen la propiedad inherente de
attemptEscape()
. Mientras que algunos métodos pueden presentar unfalse
resultado en todos los escenarios, mientras que otros pueden tener una oportunidad basada en la heurística de sus otras características intrínsecas comosize
yweight
. Entonces, ciertamente, en algún momento seattemptEscape()
vuelve trivial, ya que seguramente volverátrue
.Sin embargo, me temo que no entiendo completamente tu pregunta ... todos los animales tienen acciones y características relacionadas. Los específicos para el animal deben introducirse donde encaja. Intentar relacionar directamente a Bison con los loros no es una buena configuración de inherencia y realmente no debería ser un problema en un diseño adecuado.
fuente
Otra opción sería utilizar 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
RequiresConcreteWall()
método mencionado anteriormente lo hará.fuente
¿Qué tal recomendarCageType () como oppsed a RequireConcreteWall ()
fuente
¿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 African Bison extendiendo la clase HeavyAnimals.
Entonces, ahora la clase principal (los Animales) que se puede usar para crear otra clase base como la clase HeavyAnimal con se puede usar para crear la clase Bison africano y otros Animales pesados. Entonces, 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 acceso a la clase de Animales Pesados (esta es la base para los Animales Pesados)
fuente