In questa serie di post sul blog , Eric Lippert descrive un problema nella progettazione orientata agli oggetti usando maghi e guerrieri come esempi, dove:
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 { }
e quindi aggiunge un paio di regole:
- Un guerriero può usare solo una spada.
- Un mago può usare solo uno staff.
Quindi continua a dimostrare i problemi che si incontrano se si tenta di applicare queste regole utilizzando il sistema di tipo C # (ad esempio, rendere la Wizardclasse responsabile per assicurarsi che un mago possa usare solo uno staff). Si viola il principio di sostituzione di Liskov, si rischiano eccezioni di runtime o si finisce con un codice che è difficile da estendere.
La soluzione che propone è che la classe Player non convalida. Viene utilizzato solo per tenere traccia dello stato. Quindi, invece di dare a un giocatore un'arma:
player.Weapon = new Sword();
lo stato è modificato da se Commandsecondo Rules:
... creiamo un
Commandoggetto chiamatoWieldche prende due oggetti di stato del gioco, aPlayere aWeapon. Quando l'utente invia un comando al sistema "questa procedura guidata dovrebbe brandire quella spada", quel comando viene valutato nel contesto di un insieme diRules, che produce una sequenza diEffects. Ne abbiamo unoRuleche dice che quando un giocatore tenta di impugnare un'arma, l'effetto è che l'arma esistente, se ce n'è una, viene lasciata cadere e la nuova arma diventa l'arma del giocatore. Abbiamo un'altra regola che rafforza la prima regola, che dice che gli effetti della prima regola non si applicano quando un mago cerca di impugnare una spada.
Mi piace questa idea in linea di principio, ma ho una preoccupazione su come potrebbe essere utilizzata nella pratica.
Nulla sembra impedire a uno sviluppatore di aggirare il Commandse Rulesemplicemente impostando il parametro Weapona a Player. La Weaponproprietà deve essere accessibile dal Wieldcomando, quindi non può essere effettuata private set.
Allora, che cosa fa impedire che uno sviluppatore di fare questo? Devono solo ricordare di non farlo?