¿Es mala política tener muchas clases de "trabajo" (como clases de estrategia) que solo hacen una cosa?Patrón de estrategia y explosión de clases de "acción"
Supongamos que quiero hacer una clase Monster. En lugar de simplemente definir todo lo que quiero sobre el monstruo en una clase, trataré de identificar cuáles son sus características principales, para poder definirlas en las interfaces. Eso permitirá:
- Selle la clase si quiero. Más tarde, otros usuarios pueden simplemente crear una nueva clase y aún tener polimorfismo por medio de las interfaces que he definido. No tengo que preocuparme de cómo la gente (o yo mismo) querría cambiar/agregar características a la clase base en el futuro. Todas las clases heredan de Object e implementan herencia a través de interfaces, no de clases madre.
- Reutiliza las estrategias que estoy usando con este monstruo para otros miembros de mi mundo del juego.
Con: Este modelo es rígido. A veces nos gustaría definir algo que no se logra fácilmente simplemente tratando de armar estos "bloques de construcción".
public class AlienMonster : IWalk, IRun, ISwim, IGrowl {
IWalkStrategy _walkStrategy;
IRunStrategy _runStrategy;
ISwimStrategy _swimStrategy;
IGrowlStrategy _growlStrategy;
public Monster() {
_walkStrategy = new FourFootWalkStrategy();
...etc
}
public void Walk() { _walkStrategy.Walk(); }
...etc
}
Mi idea sería la siguiente a hacer una serie de estrategias diferentes que podrían ser utilizadas por diferentes monstruos. Por otro lado, algunos de ellos también podrían usarse con fines totalmente diferentes (es decir, podría tener un tanque que también "nada"). El único problema que veo con este enfoque es que podría conducir a una explosión de clases puras de "método", es decir, clases de estrategia que tienen como único propósito hacer esta o aquella otra acción. Por otro lado, este tipo de "modularidad" permitiría una alta reutilización de estratagemas, a veces incluso en contextos totalmente diferentes.
¿Cuál es su opinión al respecto? ¿Es este un razonamiento válido? ¿Esto es sobre-ingeniería?
Además, en el supuesto que nos gustaría hacer los ajustes apropiados al ejemplo que di anteriormente, sería mejor para definir iwalk como:
interface IWalk {
void Walk();
}
o
interface IWalk {
IWalkStrategy WalkStrategy { get; set; } //or something that ressembles this
}
es que haciendo esto no necesitaría definir los métodos en Monster, solo tendría getters públicos para IWalkStrategy (¡esto parece ir en contra de la idea de que debe encapsular todo lo que pueda!) ¿Por qué?
Gracias
Otra cosa buena de hacerlo de esta manera es que, cuando quiera pasar de A a B, puede iterar sobre su lista de interfaces de locomoción y preguntarle a cada uno el costo en lugar de hardcoding "get walk cost; get run cost" ; obtener el costo de nadar, obtener el costo de volar, obtener el costo de teletransportación ... "y incurrir en la deuda técnica de tener que mantener esa lista actualizada a medida que se implementan nuevos tipos de locomoción. –
¿Podría explicar por qué Walk, Run y Swim le parecen más como implementaciones que como interfaces? En general, ¿no debería una interfaz modelar algo que una determinada clase "puede hacer"? ISwim significaría que mi monstruo (o tanque, o cualquier clase que trate de implementar) puede nadar. Luego podría implementar varias clases de estrategia diferentes (por ejemplo) como SwimInWater SwimInAcid, etc. ¿Qué es lo que me falta aquí? –
Permítanme reformular las "Interfaces que no describen qué puede hacer algo per se". Es una buena idea usar interfaces de una manera lógica y consistente. Usar el ILocomotion para Growl sería una mala elección. Pero caminar, nadar, correr, volar, teletransportarse, etc., son solo formas de movimiento. Al abstraerlos a una interfaz de movimiento, puede simplificar mucho su código. Puede tener 100 implementaciones de ILocomotion y estar mejor que 20 implementaciones IWalk, 20 IRun, 20 ISwim, 20 ISkip, 2 ITeleport, 18 IJump. Si algo no encaja en una interfaz de ILocomotion, crea una nueva interfaz. – Felan