Supongamos que tiene lo siguiente:
+--------+ +------+
| Animal | | Food |
+-+------+ +----+-+
^ ^
| |
| |
+------+ +-------+
| Deer | | Grass |
+------+ +-------+
Deer
hereda de Animal
y Grass
hereda de Food
.
Hasta aquí todo bien. Animal
Los objetos pueden comer Food
objetos.
Ahora vamos a mezclarlo un poco. Vamos a agregar un Lion
que hereda de Animal
.
+--------+ +------+
| Animal | | Food |
+-+-----++ +----+-+
^ ^ ^
| | |
| | |
+------+ +------+ +-------+
| Deer | | Lion | | Grass |
+------+ +------+ +-------+
Ahora tenemos un problema porque Lion
podemos comer ambos Deer
y Grass
, pero Deer
no Food
es así Animal
.
Sin usar la herencia múltiple y el diseño orientado a objetos, ¿cómo se resuelve este problema?
FYI: Usé http://www.asciiflow.com para crear los diagramas ASCII.
object-oriented
object-oriented-design
Michael Irey
fuente
fuente
IHuntable
, Sheep and Cow sonIHerdable
(controlables por humanos), y Lion solo implementa IAnimal, lo que no implica ninguna de esas interfaces. AOE3 admite la consulta del conjunto de interfaces compatibles con un objeto particular (similar ainstanceof
) que permite que un programa consulte sus capacidades.Respuestas:
ES una relación = herencia
El león es un animal
TIENE relaciones A = Composición
El auto tiene una rueda
PUEDE HACER relaciones = Interfaces
Puedo comer
fuente
ICanBeEaten
oIEdible
OO es solo una metáfora que sigue el modelo del mundo real. Pero las metáforas solo van tan lejos.
Normalmente no hay una forma correcta de modelar algo en OO. Hay una manera correcta de hacerlo para un problema particular en un dominio particular y no debe esperar que funcione bien si cambia su problema, incluso si los objetos de dominio son los mismos.
Creo que este es un error común en la mayoría de los Comp. Ing. los estudiantes tienen en sus primeros años. OO no es una solución universal, solo una herramienta decente para algún tipo de problema que puede modelar su dominio razonablemente bien.
No respondí la pregunta, precisamente porque nos falta información de dominio. Pero teniendo en cuenta lo anterior, es posible que pueda diseñar algo que se adapte a sus necesidades.
fuente
Desea dividir aún más a los animales en sus subclases (o al menos en la medida en que tenga sentido para lo que está haciendo). Dado que está trabajando con lo que parecen animales básicos y dos tipos de alimentos (plantas y carne), tiene sentido usar carnívoros y herbívoros para definir mejor a un animal y mantenerlos separados. Esto es lo que redacté para ti.
Como puede ver, ambos exponen un método de comer, pero lo que comen cambia. El León ahora puede matar a un ciervo, el ciervo puede morir y devolver DeerMeat, y la pregunta original de OP sobre cómo permitir que un león coma un ciervo pero no hierba se responde sin diseñar un ecosistema completo.
Por supuesto, esto se vuelve interesante muy rápidamente porque un ciervo también podría considerarse un tipo de carne, pero para simplificar las cosas, crearía un método llamado kill () debajo de los ciervos, que devuelve una carne de ciervo, y lo pongo como un clase concreta que extiende la carne.
fuente
Eat(Plant p)
yEat(Meat m)
ambos violan LSP.Mi diseño sería así:
Debido a que los animales implementan IMeat y Deer es un animal (herbívoro), Lion, que es un animal (carnívoro) que puede comer IMeat también puede comer Deer.
El ciervo es un herbívoro, por lo que puede comer hierba porque implementa IVegetable.
Los carnívoros no pueden comer IVegeable y los herbívoros no pueden comer IMeat.
fuente
Los alimentos que puede comer un animal en realidad no forman una jerarquía, en este caso, la naturaleza no se ajustó inexcusablemente al modelado simple orientado a objetos (tenga en cuenta que incluso si lo hiciera, el animal tendría que heredar de la comida, ya que es comida).
El conocimiento de qué alimentos puede comer un animal no puede vivir completamente con ninguna de las clases, por lo que simplemente tener una referencia a algún miembro de la jerarquía alimentaria no puede ser suficiente para decirle qué cosas puede comer.
Es una relación de muchos a muchos. Esto significa que cada vez que agrega un animal, necesita descubrir qué puede comer, y cada vez que agrega un alimento, necesita averiguar qué puede comerlo. Si hay más estructura para explotar depende de qué animales y alimentos esté modelando.
La herencia múltiple tampoco resuelve esto muy bien. Necesita algún tipo de colección de cosas que un animal puede comer, o de animales que pueden comer un alimento.
fuente
Abordaré el problema desde un lado diferente: la POO se trata de comportamiento. En su caso, ¿
Grass
tiene algún comportamiento del que ser hijoFood
? Entonces, en su caso, no habráGrass
clase, o al menos, no se heredará deFood
. Además, si necesita imponer quién puede comer qué en el momento de la compilación, es cuestionable si necesitaAnimal
abstracción. Además, no es raro ver carnívoros comiendo hierba , aunque no para su sustento.Así que diseñaría esto como (sin molestarme con el arte ASCI):
IEdible
con propiedadType
, que es una enumeración de carne, planta, canal, etc. (esto no cambiará con frecuencia y no tiene ningún comportamiento específico, por lo tanto, no es necesario modelar esto como una jerarquía de clases).Animal
con métodosCanEat(IEdible food)
yEat(IEdible food)
, que son lógicos. Luego, los animales específicos pueden verificar cada vez que pueden comer alimentos dados en circunstancias dadas y luego comer esos alimentos para obtener sustento / hacer otra cosa. Además, modelaría las clases Carnívoro, Herbívoro, Omnívoro como Patrón de estrategia , que como parte de la jerarquía animal.fuente
TL; DR: Diseño o modelo con un contexto.
Creo que su pregunta es difícil porque carece de contexto del problema real que está tratando de resolver. Tiene algunos modelos y algunas relaciones, pero carece del marco en el que necesita trabajar. Sin contexto, el modelado y las metáforas no funcionan bien, dejan la puerta abierta a múltiples interpretaciones.
Creo que es más productivo centrarse en cómo se consumirán los datos. Una vez que tenga el patrón de uso de datos, es más fácil retroceder a lo que deberían ser los modelos y las relaciones.
Por ejemplo, requisitos más detallados requerirán diferentes relaciones de objeto:
Animals
eat
noFood
similarGastroliths
Chocolate
comoPoison
paraDogs
, pero no paraHumans
Si comenzamos con el ejercicio de cómo modelar la relación simple presentada, la interfaz de alimentos puede ser la mejor; y si esa es la suma total de las relaciones en el sistema, entonces está bien. Sin embargo, solo unos pocos requisitos o relaciones adicionales pueden afectar enormemente los modelos y las relaciones que funcionaron en el caso más simple.
fuente
Enfoque de composición sobre herencia de ECS:
Pseudocódigo:
Nature
es unsystem
que recorre estas entidades, buscando qué componentes tienen a través de una función de consulta generalizada.Nature
hará que las entidades con hambre de carne ataquen a otras entidades que tienen carne como alimento usando sus armas, a menos que tengan una afinidad con esa entidad. Si el ataque tiene éxito, la entidad se alimentará de su víctima, momento en el cual la víctima se convertirá en un cadáver privado de carne.Nature
hará que las entidades con hambre de plantas se alimenten de entidades que tienen plantas como alimento, siempre que existan.Quizás queremos extendernos
Grass
para tener una necesidad de luz solar y agua, y queremos introducir la luz solar y el agua en nuestro mundo. Sin embargoGrass
, no puede buscarlos directamente, ya que no tienemobility
.Animals
También puede necesitar agua, pero puede buscarla activamente ya que la tienenmobility
. Es bastante fácil seguir ampliando y cambiando este modelo sin romper en cascada todo el diseño, ya que solo agregamos nuevos componentes y ampliamos el comportamiento de nuestros sistemas (o la cantidad de sistemas).fuente
Como la mayoría de las cosas, depende .
Depende de lo que vea 'este problema'.
Si está preguntando sobre el problema general de implementación, la respuesta dependerá de las capacidades de su entorno. Las interfaces IFood e IAnimal podrían funcionar, con una subclase EdibleAnimal que implementa ambas interfaces. Si su entorno no admite interfaces, simplemente haga que Animal herede de Food.
Si está preguntando sobre este problema de diseño específico, simplemente haga que Animal herede de Food. Es lo más simple que podría funcionar.
Si está preguntando acerca de estos conceptos de diseño, la respuesta depende en gran medida de lo que pretenda hacer con el modelo. Si es para un videojuego perro-come-perro o incluso una aplicación para rastrear los horarios de alimentación en un zoológico, podría ser suficiente para trabajar. Si se trata de un modelo conceptual para patrones de comportamiento animal, probablemente sea un poco superficial.
fuente
La herencia debe usarse para algo que siempre es otra cosa y que no puede cambiar. La hierba no siempre es comida. Por ejemplo, yo no como hierba.
La hierba desempeña el papel de un alimento para ciertos animales.
fuente
Acaba de encontrar la limitación básica de OO.
OO funciona bien con estructuras jerárquicas. Pero una vez que te alejas de las jerarquías estrictas, la abstracción no funciona tan bien.
Sé todo sobre las composiciones de metamorfosis, etc., que se utilizan para sortear estas limitaciones, pero son torpes y, lo que es más importante, conducen a un código oscuro y difícil de seguir.
Las bases de datos relacionales se inventaron principalmente para escapar de las limitaciones de las estructuras jerárquicas estrictas.
Para tomar su ejemplo, el césped también podría ser un material de construcción, una materia prima para el papel, un material de ropa, una maleza o un cultivo.
Un ciervo puede ser una mascota, ganado, un animal de zoológico o una especie protegida.
Un león también podría ser un animal de zoológico o una especie protegida.
La vida no es simple.
fuente
¿Qué problema? ¿Qué hace este sistema? Hasta que responda eso, no tengo idea de qué clases pueden ser necesarias. ¿Estás tratando de modelar una ecología, con carnívoros, herbívoros y plantas, proyectando poblaciones de especies hacia el futuro? ¿Estás tratando de que la computadora juegue 20 preguntas?
Es una pérdida de tiempo comenzar el diseño antes de que se hayan definido los casos de uso. He visto esto llevado a extremos ridículos cuando un equipo de unos diez comenzó a producir un modelo OO de una aerolínea utilizando software a través de imágenes. Trabajaron durante dos años modelando sin ningún problema comercial real en mente. Finalmente, el cliente se cansó de esperar y le pidió al equipo que resolviera un problema real. Todo ese modelado fue completamente inútil.
fuente