Em esta série de posts , Eric Lippert descreve um problema no design orientado a objetos usando assistentes e guerreiros como exemplos, em que:
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 depois adiciona algumas regras:
- Um guerreiro só pode usar uma espada.
- Um assistente pode usar apenas uma equipe.
Ele então demonstra os problemas com os quais você se depara se tentar aplicar essas regras usando o sistema do tipo C # (por exemplo, responsabilizando a Wizard
classe por garantir que um assistente possa usar apenas uma equipe). Você viola o princípio de substituição de Liskov, corre o risco de exceções em tempo de execução ou acaba com um código difícil de estender.
A solução que ele apresenta é que nenhuma validação é feita pela classe Player. É usado apenas para rastrear o estado. Então, em vez de dar uma arma a um jogador:
player.Weapon = new Sword();
o estado é modificado por se de Command
acordo com Rule
s:
... criamos um
Command
objeto chamadoWield
que leva dois objetos de estado do jogo, aPlayer
e aWeapon
. Quando o usuário emite um comando para o sistema “esse assistente deve usar essa espada”, esse comando é avaliado no contexto de um conjunto deRule
s, que produz uma sequência deEffect
s. Temos umaRule
que diz que quando um jogador tenta empunhar uma arma, o efeito é que a arma existente, se houver, é descartada e a nova arma se torna a arma do jogador. Temos outra regra que fortalece a primeira regra, que diz que os efeitos da primeira regra não se aplicam quando um mago tenta empunhar uma espada.
Eu gosto dessa idéia em princípio, mas tenho uma preocupação sobre como ela pode ser usada na prática.
Nada parece impedir que um desenvolvedor de iludirem o Commands
e Rule
é simplesmente definindo o Weapon
em um Player
. A Weapon
propriedade precisa estar acessível pelo Wield
comando, portanto, não pode ser feita private set
.
Então, o que impede um desenvolvedor de fazer isso? Eles apenas precisam se lembrar de não?