En esta serie de publicaciones de blog , Eric Lippert describe un problema en el diseño orientado a objetos utilizando magos y guerreros como ejemplos, donde:
abstract class Weapon { }
sealed class Staff : Weapon { }
sealed class Sword : Weapon { }
abstract class Player
{
public Weapon Weapon { get; set; }
}
sealed class Wizard : Player { }
sealed class Warrior : Player { }
y luego agrega un par de reglas:
- Un guerrero solo puede usar una espada.
- Un asistente solo puede usar un bastón.
Luego continúa demostrando los problemas con los que se encuentra si intenta hacer cumplir estas reglas utilizando el sistema de tipo C # (por ejemplo, hacer que la Wizardclase sea responsable de asegurarse de que un asistente solo pueda usar un bastón). Viola el Principio de sustitución de Liskov, arriesga excepciones de tiempo de ejecución o termina con un código que es difícil de extender.
La solución que se le ocurre es que la clase Player no realiza ninguna validación. Solo se usa para rastrear el estado. Luego, en lugar de darle un arma a un jugador:
player.Weapon = new Sword();
el estado se modifica por Commandsy según Rules:
... hacemos un
Commandobjeto llamadoWieldque toma dos objetos de estado del juego, aPlayery aWeapon. Cuando el usuario emite un comando para el sistema "este asistente debería empuñar esa espada", entonces ese comando se evalúa en el contexto de un conjunto deRules, que produce una secuencia deEffects. Tenemos unoRuleque dice que cuando un jugador intenta empuñar un arma, el efecto es que el arma existente, si la hay, se cae y la nueva arma se convierte en el arma del jugador. Tenemos otra regla que fortalece la primera regla, que dice que los efectos de la primera regla no se aplican cuando un mago intenta empuñar una espada.
En principio, me gusta esta idea, pero me preocupa cómo podría usarse en la práctica.
Nada parece impedir que un desarrollador se sustraigan a la Commandsy Rulees simplemente fijando el Weaponsobre una Player. El comando Weapondebe poder acceder a la propiedad Wield, por lo que no se puede hacer private set.
Por lo tanto, lo que hace evitar que un desarrollador de hacer esto? ¿Solo tienen que recordar no hacerlo?