In questa serie di post del blog , Eric Lippert descrive un problema nella progettazione orientata agli oggetti usando come esempi esempi di procedure guidate e guerrieri:
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.
- Una procedura guidata può utilizzare solo uno staff.
Quindi prosegue mostrando i problemi che si incontrano se si tenta di applicare queste regole utilizzando il sistema di tipo C # (ad esempio, rendendo la classe Wizard
responsabile di assicurarsi che una procedura guidata possa utilizzare solo uno staff). Violi il Principio di sostituzione di Liskov, rischi le eccezioni di run-time o finisci con un codice che è difficile estendere.
La soluzione che ha in mente è che la classe Player non esegue alcuna convalida. Viene utilizzato solo per tenere traccia dello stato. Quindi, invece di dare a un giocatore un'arma di:
player.Weapon = new Sword();
Lo stato è modificato da Command
s e secondo Rule
s:
...we make a
Command
object calledWield
that takes two game state objects, aPlayer
and aWeapon
. When the user issues a command to the system “this wizard should wield that sword”, then that command is evaluated in the context of a set ofRule
s, which produces a sequence ofEffect
s. We have oneRule
that says that when a player attempts to wield a weapon, the effect is that the existing weapon, if there is one, is dropped and the new weapon becomes the player’s weapon. We have another rule that strengthens the first rule, that says that the first rule’s effects do not apply when a wizard tries to wield a sword.
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 eludere Commands
e Rule
s semplicemente impostando Weapon
su Player
. La proprietà Weapon
deve essere accessibile dal comando Wield
, quindi non può essere resa private set
.
Quindi, cosa fa impedisce a uno sviluppatore di farlo? Devono solo ricordarsi di non farlo?