¿Es este mal diseño de POO para una simulación que involucra interfaces?

13

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 Interfaces.

( Todavía estoy abstrayendo aquí y aún no tengo implementación de código, así que es más bien una cuestión de diseño de POO ... ¡Creo!)

¿Tengo razón al buscar 'comportamiento común' entre estas clases e implementarlas como interfaces ?

Por ejemplo, mordedura de vampiros y lobos ... ¿debería tener una interfaz de mordida?

public class Vampire : Villain, IBite, IMove, IAttack

Igualmente para camiones ...

public class Truck : Vehicle, IMove

Y para los humanos ...

public class Man : Human, IMove, IDead

¿Mi pensamiento está aquí? (Aprecie su ayuda)

    
pregunta user3396486 29.09.2016 - 19:49

3 respuestas

33

En general, desea tener interfaces para las características comunes de su clase.

Estoy semi-de acuerdo con @Robert Harvey en los comentarios, quienes dijeron que generalmente las interfaces representan características más abstractas de las clases. Sin embargo, a partir de ejemplos más concretos, creo que es una buena manera de empezar a pensar en abstracto.

Si bien tu ejemplo es técnicamente correcto (es decir, sí, tanto los vampiros como los lobos muerden, por lo que puedes tener una interfaz para eso), hay una cuestión de relevancia. Cada objeto tiene miles de características (por ejemplo, los animales pueden tener piel, pueden nadar, pueden trepar árboles, etc.). ¿Harás una interfaz para todos ellos? Muy menos probable.

Por lo general, las interfaces para cosas que tienen sentido se agrupan en una aplicación como un todo. Por ejemplo, si está creando un juego, puede tener una gran 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 en cómo va a utilizar esa interfaz, y 2 clases que tengan un método en común no es una razón suficiente para crear una interfaz.

    
respondido por el Paul92 29.09.2016 - 20:31
28

Parece que estás creando un grupo 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 utilizan. Los clientes deciden si algo debe ser algo que pueda moverse y atacar.

Si tengo una clase Combat con un método fight() , es probable que ese método tenga la necesidad de llamar tanto a move() como a attack() en el mismo objeto. Eso sugiere fuertemente la necesidad de una interfaz ICombatant a la que fight() pueda llamar a través de move() y attack() . Esto es más limpio que fight() tomando un objeto IAttack y convirtiéndolo en IMove para ver si también se puede mover.

Eso no significa que no puedas tener interfaces IMove IAttack . Solo espero que no los hagas sin que algún cliente los necesite. A la inversa, si ningún cliente necesita hacer que un objeto se mueva y ataque, no se necesita ICombatant .

Esta forma simple de ver las interfaces a menudo se pierde porque a la gente le gustan los siguientes ejemplos. Las primeras interfaces a las que estamos expuestos están en las bibliotecas. Desafortunadamente, las bibliotecas no tienen idea de lo que son sus clientes. Así que solo pueden adivinar las necesidades de sus clientes. No es el mejor ejemplo a seguir.

    
respondido por el candied_orange 29.09.2016 - 20:31
3

Considere si será común tener colecciones de objetos con diferentes combinaciones de habilidades, y si el código podría querer realizar una acción sobre esos elementos, dentro de una colección, que lo respalden . Si es así, y si hubiera un "comportamiento predeterminado" razonable para los objetos que no tienen un 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 algunos tipos de criaturas pueden tener Woozles, y uno quiere que dichas criaturas tengan una propiedad NumerOfWoozles . Si tal propiedad estuviera en una interfaz que solo fuera implementada por criaturas que pueden tener Woozles, entonces el código que deseaba encontrar el número total de Woozles en poder de una colección de criaturas de tipos mixtos tendría que decir algo como:

int total = 0;
foreach (object it in creatures)
{
   IWoozleCountable w = trycast(it, IWoozleCountable);
   if (w != null) total += w.WoozleCount;
}

Sin embargo, si WoozleCount fuera un miembro de Creature / ICreature, aunque pocos subtipos anularían la implementación predeterminada de WoozleCount de Creature que siempre devuelve cero, el código podría simplificarse a:

int total = 0;
foreach (ICreature it in creatures)
   total += it.WoozleCount;

Si bien algunas personas pueden molestarse con la idea de que cada Creature implemente una propiedad WoozleCount que solo es realmente útil para algunos subtipos, la propiedad sería significativa para todos los tipos, sea o no útil con elementos que se sabe son de esos tipos, y yo consideraría que la interfaz del "fregadero de la cocina" tiene menos olor a código que el operador de trycast.

    
respondido por el supercat 30.09.2016 - 17:42

Lea otras preguntas en las etiquetas