Cuando utiliza el concepto de polimorfismo, crea una jerarquía de clases y, utilizando la referencia de los padres, llama a las funciones de la interfaz sin saber qué tipo específico tiene el objeto. Esto es genial. Ejemplo:
Tienes una colección de animales y llamas a todas las funciones de los animales eat
y no te importa si es un perro comiendo o un gato. Pero en la misma jerarquía de clases hay animales que tienen otros, además de heredados e implementados de la clase Animal
, por ejemplo makeEggs
, getBackFromTheFreezedState
etc. Entonces, en algunos casos en su función, es posible que desee conocer el tipo específico para llamar a comportamientos adicionales.
Por ejemplo, en caso de que sea la hora de la mañana y si es solo un animal, llame eat
, de lo contrario, si es un humano, llame primero washHands
, getDressed
y solo luego llame eat
. ¿Cómo manejar estos casos? El polimorfismo muere. Debe averiguar el tipo de objeto, que suena como un olor a código. ¿Existe un enfoque común para manejar estos casos?
Eater
interfaz con eleat()
método, entonces, como cliente, no le importa que unaHuman
implementación tenga que llamar primerowashHands()
ygetDressed()
es una implementación de detalles de esta clase. Si, como cliente, le importa este hecho, lo más probable es que no esté utilizando la herramienta correcta para el trabajo.getDressed
anteseat
, ese no es el caso para el almuerzo. Dependiendo de sus circunstancias,washHands();if !dressed then getDressed();[code to actually eat]
podría ser la mejor manera de implementar esto para un ser humano. Otra posibilidad es ¿qué pasa si otras cosas requieren esowashHands
y / ogetDressed
se llaman? ¿Y si tienesleaveForWork
? Es posible que necesite estructurar el flujo de su programa para tenerlo de modo que se llame mucho antes de eso de todos modos.Respuestas:
Depende Lamentablemente no hay una solución genérica. Piense en sus requisitos y trate de descubrir qué deberían hacer estas cosas.
Por ejemplo, dijiste en la mañana que diferentes animales hacen cosas diferentes. ¿Qué tal se introduce un método
getUp()
oprepareForDay()
o algo por el estilo. Luego puede continuar con el polimorfismo y dejar que cada animal ejecute su rutina matutina.Si desea diferenciar entre animales, entonces no debe almacenarlos indiscriminadamente en una lista.
Si nada más funciona, entonces puede probar el Patrón de visitante , que es una especie de truco para permitir una especie de despacho dinámico en el que puede enviar un visitante que recibirá devoluciones de llamada exactas de los animales. Sin embargo, destacaría que este debería ser el último recurso si todo lo demás falla.
fuente
Esta es una buena pregunta y es el tipo de problema que mucha gente trata de entender cómo usar OO. Creo que la mayoría de los desarrolladores luchan con esto. Ojalá pudiera decir que la mayoría lo supera, pero no estoy seguro de que sea así. La mayoría de los desarrolladores, en mi experiencia, terminan usando bolsas de propiedades pseudo-OO .
Primero, déjame ser claro. Esto no es tu culpa. La forma en que normalmente se enseña OO es altamente defectuosa. El
Animal
ejemplo es el principal delincuente, IMO. Básicamente, decimos, hablemos de los objetos, qué pueden hacer. UnaAnimal
lataeat()
y puedespeak()
. Súper. Ahora crea algunos animales y codifica cómo comen y hablan. Ahora sabes OO, ¿verdad?El problema es que esto viene en OO desde la dirección equivocada. ¿Por qué hay animales en este programa y por qué necesitan hablar y comer?
Me cuesta pensar en un uso real para un
Animal
tipo. Estoy seguro de que existe, pero analicemos algo sobre lo que creo que es más fácil razonar: una simulación de tráfico. Supongamos que queremos modelar el tráfico en varios escenarios. Aquí hay algunas cosas básicas que debemos tener para poder hacerlo.Podemos profundizar en todo tipo de cosas para peatones y trenes, pero lo mantendremos simple.
Vamos a considerar
Vehicle
. ¿Qué capacidades necesita el vehículo? Necesita viajar en un camino. Necesita poder detenerse en las señales. Necesita poder navegar en las intersecciones.Probablemente esto sea demasiado simple, pero es un comienzo. Ahora. ¿Qué pasa con todas las otras cosas que podría hacer un vehículo? Pueden salir de una carretera y convertirse en una zanja. ¿Es eso parte de la simulación? No. No lo necesito. Algunos automóviles y autobuses tienen sistemas hidráulicos que les permiten rebotar o arrodillarse, respectivamente. ¿Es eso parte de la simulación? No. No lo necesito. La mayoría de los automóviles queman gasolina. Algunos no. ¿La planta de energía es parte de la simulación? No. No lo necesito. ¿Tamaño de la rueda? No lo necesito ¿Navegacion GPS? Sistema de infoentretenimiento? No los necesito.
Solo necesita definir comportamientos que va a utilizar. Con ese fin, creo que a menudo es mejor construir interfaces OO a partir del código que interactúa con ellas. Comienza con una interfaz vacía y luego comienza a escribir el código que llama a los métodos inexistentes. Así es como sabe qué métodos necesita en su interfaz. Luego, una vez que haya hecho eso, vaya y comience a definir clases que implementen estos comportamientos. Los comportamientos que no se usan son irrelevantes y no necesitan ser definidos.
El objetivo de OO es que puede agregar nuevas implementaciones de estas interfaces más adelante sin cambiar el código de llamada. La única forma en que funciona es si las necesidades del código de llamada determinan lo que pasa en la interfaz. No hay forma de definir todos los comportamientos de todas las cosas posibles que podrían pensarse más adelante.
fuente
TL; DR:
Piense en una abstracción y métodos que se apliquen a todas las subclases y cubran todo lo que necesita.
Primero quedémonos con su
eat()
ejemplo.Es una propiedad de ser humano que, como condición previa para comer, los humanos quieren lavarse las manos y vestirse antes de comer. Si quieres que alguien venga a desayunar contigo, no les digas que se laven las manos y se vistan, lo hacen solos cuando los invitas o responden: "No, no puedo ir. Ya no me lavé las manos y todavía no estoy vestida ".
Volver al software:
Como
Human
ejemplo no comer sin las condiciones previas, tendría laHuman
'seat()
método de hacerwashHands()
ygetDressed()
si eso no se ha hecho. No debería ser su trabajo comoeat()
interlocutor saber sobre esa peculiaridad. La alternativa del terco humano sería lanzar una excepción ("¡No estoy preparado para comer!") Si no se cumplen las condiciones previas, dejándolo frustrado, pero al menos informado de que comer no funcionó.¿Qué hay de
makeEggs()
?Recomiendo cambiar tu forma de pensar. Probablemente quieras ejecutar las tareas matutinas programadas de todos los seres. Nuevamente, como la persona que llama no debería ser su trabajo saber cuáles son sus deberes. Por lo tanto, recomendaría un
doMorningDuties()
método que implementen todas las clases.fuente
La respuesta es bastante simple.
No necesita manejarlo porque no serviría para nada. Una interfaz generalmente se diseña dependiendo de cómo se vaya a usar. Si su interfaz no define lavarse las manos, entonces no le importa como interlocutor; si lo hubiera hecho, lo habría diseñado de manera diferente.
Por ejemplo, en pseudocódigo:
Ahora lo implementa
IMorningPerformer
paraAnimal
simplemente comer, yHuman
también lo implementa para lavarse las manos y vestirse. La persona que llama de su método MorningTime podría importarle menos si es humano o un animal. Todo lo que quiere es la rutina matutina realizada, que cada objeto hace admirablemente gracias a OO.O lo hace?
¿Por qué están asumiendo eso? Creo que esto podría ser una suposición errónea.
Sí, generalmente se resuelve con una clase o jerarquía de interfaz cuidadosamente diseñada. Tenga en cuenta que en el ejemplo anterior no hay nada que contradiga su ejemplo tal como lo ha dado, sin embargo, probablemente se sentirá insatisfecho, porque ha hecho algunas suposiciones más que no escribió en la pregunta al momento de escribir. , y estos supuestos son probablemente violados.
Es posible ir a una madriguera de conejos ajustando sus suposiciones y modificando la respuesta para satisfacerlas, pero no creo que sea útil.
Diseñar buenas jerarquías de clases es difícil y requiere mucha información sobre su dominio comercial. Para dominios complejos, uno pasa por dos, tres o incluso más iteraciones, ya que refinan su comprensión de cómo interactúan las diferentes entidades en su dominio comercial, hasta llegar a un modelo adecuado.
Ahí es donde faltan ejemplos simplistas de animales. Queremos enseñar de manera simple, pero el problema que estamos tratando de resolver no es obvio hasta que profundice, es decir, tenga consideraciones y dominios más complejos.
fuente