Estoy diseñando mi propio pequeño programa OOP para simular vampiros, lobos, humanos y camiones y estoy tratando de implementar mi propia comprensión limitada de las interfaces.
( Todavía estoy abstrayendo aquí y todavía no tengo implementación de código, así que es más bien una cuestión de diseño de OOP ... ¡creo!)
¿Estoy en lo cierto al buscar 'comportamiento común' entre estas clases e implementarlas como interfaces ?
Por ejemplo, los vampiros y los lobos muerden ... ¿entonces debería tener una interfaz de mordisco?
public class Vampire : Villain, IBite, IMove, IAttack
Del mismo modo para camiones ...
public class Truck : Vehicle, IMove
Y para los humanos ...
public class Man : Human, IMove, IDead
¿Mi pensamiento está aquí? (Aprecio tu ayuda)
object-oriented
interfaces
usuario3396486
fuente
fuente
IEnumerable
,IEquatable
, etc.Respuestas:
En general, desea tener interfaces para las características comunes de su clase.
Estoy semi-de acuerdo con @Robert Harvey en los comentarios, quien dijo que generalmente las interfaces representan características más abstractas de las clases. Sin embargo, encuentro a partir de ejemplos más concretos una buena forma de comenzar a pensar en abstracto.
Si bien su ejemplo es técnicamente correcto (es decir, sí, los vampiros y los lobos muerden, por lo que puede tener una interfaz para eso), hay una cuestión de relevancia. Cada objeto tiene miles de características (por ejemplo, los animales pueden tener pelaje, nadar, trepar a los árboles, etc.). ¿Harás una interfaz para todos ellos? Muy menos probable.
Por lo general, desea interfaces para cosas que tienen sentido agruparse en una aplicación como un todo. Por ejemplo, si está creando un juego, puede tener una variedad de objetos IMove y actualizar su posición. Si no quieres hacer eso, tener la interfaz IMove es bastante inútil.
El punto es, no sobre ingeniero. Debe pensar cómo va a usar esa interfaz, y 2 clases que tienen un método en común no es una razón suficiente para crear una interfaz.
fuente
IBite
no es particularmente útil, pero es posible que deseeIAttack
para poder trabajar en todas las cosas que hacen ataques, oIUpdate
para que pueda ejecutar las actualizaciones para todo, oIPhysicsEnabled
para que pueda aplicarles física, etc.Parece que estás creando un montón de interfaces de método único . Esto está bien a primera vista, pero tenga en cuenta que las interfaces no son propiedad de las clases que las implementan. Son propiedad de los clientes que los usan. Los clientes deciden si algo debe ser algo que pueda moverse y atacar.
Si tengo una
Combat
clase con unfight()
método, ese método probablemente necesite llamar a ambosmove()
yattack()
en el mismo objeto. Esto sugiere fuertemente la necesidad de unaICombatant
interfaz quefight()
puede llamarmove()
yattack()
a través. Esto es más limpio quefight()
tomar unIAttack
objeto y lanzarloIMove
para ver si también puede moverse.Eso no significa que no puedas tener también
IMove
IAttack
interfaces. Solo espero que no los hagas sin que algún cliente los necesite. Por el contrario, si ningún cliente necesita hacer que un objeto se mueva y ataque, entoncesICombatant
no es necesario.Esta forma simple de mirar las interfaces a menudo se pierde porque a las personas les gusta seguir ejemplos. Las primeras interfaces a las que estamos expuestos están en las bibliotecas. Desafortunadamente, las bibliotecas no tienen idea de cuáles son sus clientes. Por lo tanto, solo pueden adivinar las necesidades de sus clientes. No es el mejor ejemplo a seguir.
fuente
Considere si será común tener colecciones de objetos con diferentes combinaciones de habilidades, y si el código puede querer realizar una acción sobre esos elementos, dentro de una colección, que lo soportan . Si es así, y si hubiera un "comportamiento predeterminado" sensible para los objetos que no tienen soporte útil para alguna acción, puede ser útil tener interfaces implementadas por una amplia gama de clases, no solo aquellas que pueden comportarse de manera útil.
Por ejemplo, supongamos que solo unos pocos tipos de criaturas pueden tener Woozles, y uno quiere que tales criaturas tengan una
NumerOfWoozles
propiedad. Si dicha propiedad estuviera en una interfaz que solo fueran implementadas por criaturas que pueden tener Woozles, entonces el código que quería encontrar el número total de Woozles en poder de una colección de criaturas de tipos mixtos tendría que decir algo como:Sin embargo, si WoozleCount fuera miembro de Creature / ICreature, a pesar de que pocos subtipos anularían la implementación de WoozleCount predeterminada de Creature que siempre devuelve cero, el código podría simplificarse para:
Si bien algunas personas pueden irritarse ante la idea de que cada Criatura implemente una propiedad WoozleCount que realmente solo es útil para unos pocos subtipos, la propiedad sería significativa para todos los tipos, ya sea que sea útil o no con elementos que se sabe que son de esos tipos, y consideraría que la interfaz del "fregadero de la cocina" tiene menos olor a código que el operador trycast.
fuente