OO projeto, / aberto fechado princípio questão
-
01-07-2019 - |
Pergunta
Eu estive pensando sobre isso orientada a objeto pergunta projeto por um tempo agora e ter incapaz de chegar a uma solução satisfatória, por isso pensei que eu ia jogá-lo abrir às multidões aqui para algumas opiniões.
Eu tenho um Jogo classe que representa um jogo de tabuleiro baseado em turnos, podemos supor que é semelhante ao monopólio para os fins da presente questão. Na minha concepção Eu tenho um Jogador classe que contém um método TakeTurn .
O Jogo percorre todos Jogador S e chama o método TakeTurn para fazer todas as coisas necessárias para completar a sua vez. Eu quero ser capaz de ter n número de jogadores, e ser capaz de definir um número arbitrário de que eles sejam jogadores de computador. Então, meu pensamento era ter uma classe HumanPlayer e um ComputerPlayer classe, sendo que ambos derivam Player.
O Jogo só conhece o Jogador classe e simplesmente chama o TakeTurn método em cada Jogador , por sua vez. Meu problema vem no fato de que ComputerPlayer objetos pode ser completamente automatizado, mantendo ou seja, com o exemplo Monopoly, pode decidir comprar uma propriedade usando alguma peça de lógica. Agora, com o HumanPlayer objeto, ele precisa obter uma entrada do usuário real para ser capaz de comprar um imóvel, por exemplo, o que parece implicar uma interface diferente e potencialmente significa que eles devem não derive
Eu não tenho sido capaz de chegar a uma boa solução para o problema sem ter o Jogo class conhecer as implementações reais dos diferentes Jogador aulas explicitamente. Eu sempre poderia fazer a suposição no Jogo classe que não só nunca será jogadores humanos e computadores e efetivamente fechá-lo para a extensão, mas não parece ser uma boa programação OO.
Todas as opiniões sobre este seria apreciada.
Solução
Eu acho que você não deve deixar a alça classe Jogo IO. Deste modo, o (bloqueio) TakeTurn método irá esconder do tabuleiro dos meios de aplicação. ele pode usar outros objetos para se comunicar com o usuário.
Toda a classe jogo deve preocupar-se com é o estado da placa e no turn. os jogadores devem todos implementar uma interface única Player, e esconder toda a execução do jogo.
Outras dicas
Se o jogo está a gerir o estado do jogo e fazendo I / O, o jogo está fazendo muito.
Você quer jogo a ser fortemente centrada em regras e voltas apenas e mudanças de estado. O jogo não sabe o que um jogador é; ele só sabe que tem jogadores.
Você quer jogadores para examinar o estado do jogo e executar as ações legais durante os seus turnos.
jogadores humanos eo jogo como um todo ambos compartilhar um pacote comum I / O que estado game shows e pede seres humanos para a sua entrada.
Você pode fazer bom uso do Observable
Java, tornando o I / O pacote um Observer
do Jogo. Dessa forma, as mudanças de estado do jogo são relatados para o I / O para exibição ou exploração madeireira ou ambos.
Eu provavelmente não tem duas classes HumanPlayer
e ComputerPlayer
, mas uma única classe Player
que é configurado no momento da criação com a estratégia de entrada adequada.
A forma como a informação o jogador obtém para decidir seu movimento no próximo turno do jogo é a única coisa que varia (a partir da descrição do problema original, pelo menos), então apenas encapsular que em um abstração separada.
Whatever classe de alto nível que configura o jogo também deve criar os dois conjuntos de jogadores (um humano, um outro simulado por computador), com a estratégia de entrada adequada para cada um, e depois simplesmente dar a estes jogadores objetos ao objeto jogo . A classe jogo vai só então chamar o método TakeTurn
na lista dada de jogadores, para cada novo turno.
Em vez de dizer a classe jogo há sempre apenas uma humana, por que não deixá-lo chegar que a entrada durante o menu / inicialização do jogo? Se houver mais jogadores, que podem ser decididas através de alguma forma de entrada (seleccionar jogadores no menu), antes da inicialização da classe jogo.
A interface que Jogador apresenta a Jogo é ortogonal ao comportamento dos derivados Jogador classes.
O fato de que a implementação de TakeTurn varia dependendo do tipo de concreto do Jogador objeto não deve ser um motivo de preocupação.
Eu acho que o Game
Classe não deve preocupação com qualquer implementações das classes do jogador, e também ignorar o User Interface.
Todas as necessidades de entrada do usuário para ser tratado pela classe HumanPlayer
.
Eu diria que, a Jogo classe deve se importa se este é um jogador do computador ou um jogador humano. Ele deve sempre chamar TakeTurn na próxima aula jogador. Se este é um jogador humano, é da responsabilidade do Jogador classe, para se comunicar com o usuário e pedir ao usuário o que fazer. Isso significa que ele irá bloquear até que o usuário fez a sua mente. Como a interação geralmente UI tem lugar no segmento principal de uma aplicação, é apenas importante que um bloqueio TakeTurn não vai bloquear o aplicativo como uma entrada inteira, caso contrário, o usuário não pode ser processado enquanto Jogo aguarda TakeTurn .
Em vez do Jogo classe chamando TakeTurn em todos os jogadores os jogadores devem chamar TakeTurn na Jogo classe e o Jogo classe deve validar se o jogador certo é tomar a sua vez.
Isso deve ajudar a resolver o usuário e Leitor problema do computador.
Im não tenho certeza se é isso que você quer
public abstract class Player
{
int position;
DecisionMaker decisionDependency;
...
public void TakeTurn()
{
position += RollDice();
GameOption option GetOptions(position);
MakeDescion(option);
}
protected int RollDice()
{
//do something to get the movement
}
protected abstract void MakeDecision(GameOption option);
}
Public class ComputerPlayer : Player
{
public ComputerPlayer()
{
decisionDependency = new AIDecisionMaker();
}
protected override void void MakeDecision(GameOption option)
{
decisionDependency.MakeDecision(option);
//do stuff, probably delgate toan AI based dependency
}
}
Public class HumanPlayer : Player
{
public HumanPlayer()
{
decisionDependency = new UIDecisionMaker();
}
protected override void void MakeDecision(GameOption option)
{
decisionDependency.MakeDecision(option);
//do stuff, probably interacting with the a UI or delgate to a dependency
}
}