Passando uma lista de opções com diferentes argumentos para cada escolha

5

Eu estou escrevendo um jogo de cartas, no qual eu separei o "Core Logic" do jogo da interface do usuário. A comunicação com o player da parte lógica central é feita por meio de retornos de chamada que a interface do usuário implementa e manipula (em um thread separado, se necessário). A biblioteca lógica central chama esses retornos de chamada, a IU os manipula, passa um objeto que representa os dados que precisam ser retornados e os retorna.

O que eu estou atualmente preso é implementar a capacidade do Core Logic para dar ao jogador uma "escolha" entre as ações possíveis. Por exemplo, a lógica central pode querer que o jogador selecione entre as seguintes 3 ações do jogo:

  • Compre 5 cartas do baralho A
  • Compre 2 cartas do baralho B
  • Alocar 4 pontos no intervalo 1

O ponto é que cada ação pode ser bem diferente da outra. Como posso representar essa informação como um objeto em c #? Eu posso pensar em 4 maneiras feias ...

  1. Basta passar uma lista de strings contendo o texto de escolha para a interface do usuário e fazer com que a interface do usuário as exiba. Em seguida, o usuário seleciona entre elas:

Esta é a abordagem mais simples (e muito tentadora), mas isso seria misturar a interface do usuário com o Core Logic, que vai contra a ideia principal por trás do design.

  1. Passa um objeto contendo um enum e um objeto, cujo tipo real depende do valor do enum (o enum é algo como Draw, Allocate, etc., e o objeto contém coisas como o número de cards e qual deck para a escolha do Draw e o número de pontos e qual bucket para o comando Alocar)

Mas isso envolve muitas declarações, tipoffing e reflexão, entre outros problemas, o que é feio. Isso também significa que eu teria que fazer uma classe separada para quase todo tipo de enum de escolha, o que também é feio porque cada classe seria apenas uma lista de valores.

  1. Passe um dicionário contendo chaves e valores de string, o conteúdo do dicionário dependendo do tipo de escolha que se pode fazer.

Isso ainda envolverá muito typecasting e eu também perderei segurança de tipo, mas pelo menos eu não teria que fazer uma classe para cada escolha.

  1. Passa um objeto contendo um enum e um objeto dinâmico contendo um tipo anônimo cujos campos dependem de qual é o valor do enum.

Isso torna o manuseio do material na interface do usuário muito mais simples e eu não teria que fazer várias classes, mas depois perdia segurança de tipo e verificações básicas durante o tempo de compilação.

Existe algum tipo de padrão que as pessoas costumam usar quando enfrentam este problema, ou eu deveria apenas pegar meu veneno e ir atrás dele? Talvez eu deva repensar toda a minha abordagem para a coisa toda?

Em C, eu provavelmente estaria usando uma união de estruturas para esse tipo de coisa. Eu não tenho certeza de como eu iria fazer isso em C #. Talvez eu esteja pensando demais em C?

    
por 9a3eedi 08.11.2015 / 16:41
fonte

2 respostas

2

Eu sou para 2, mas com pequena modificação. Eu não usaria enum . Você pode usar o GetType() da mesma forma. Se você adicionasse enum ao mix, duplicaria as informações de tipo contidas no objeto.

Além disso, criaria um Visitante para a classe Choice . Isso removerá a necessidade de reflexão e fornecerá uma maneira segura de criar uma representação textual para cada opção. Ele também garantirá que, quando você adicionar um novo tipo de opção, o compilador forçará você a implementar todo o código relacionado, como criar uma representação textual para ele. E não vejo como ter muitas classes é ruim. É realmente bom, contanto que essas classes sejam coesas.

E não acho que esses objetos contenham apenas dados. Eles podem muito bem ser objetos fazendo a ação real. Basta colocar o método Execute() neles, e eles podem ser os que desenham os cartões de baralhos ou alocam pontos para depósitos.

    
por 09.11.2015 / 08:35
fonte
2

Eu usaria uma classe base com classes herdadas ou uma interface com implementações concretas para distinguir o tipo de escolha que o usuário pode fazer. As interfaces fariam mais sentido se você precisasse realizar algum tipo de operação na escolha, mas como isso não está no escopo de sua pergunta, aqui está um exemplo da opção anterior usando uma classe base com herança.

public class ChoiceOption
{
}

public class DrawChoiceType : ChoiceOption
{
    public int NumberToDraw { get; set; }
    public Deck DrawFrom { get; set; }
}

public class AllocatePointsChoiceType : ChoiceOption
{
    public int PointsToAllocate { get; set; }
    public Bucket AllocatePointsTo { get; set; }
}

Com essa estrutura, seu mecanismo de jogo passará a lista de opções para o jogador e lidará com a escolha feita desta forma:

List<ChoiceOption> choicesAvailableToPlayer = new List<ChoiceOption>();
choicesAvailableToPlayer.Add(new DrawChoiceType() { DrawFrom = Decks.DeckA, NumberToDraw = 5 });
choicesAvailableToPlayer.Add(new DrawChoiceType() { DrawFrom = Decks.DeckB, NumberToDraw = 2 });
choicesAvailableToPlayer.Add(new AllocatePointsChoiceType () { AllocatedPointsTo = Buckets.Bucket1, PointsToAllocate = 4 });
ChoiceOption playerChose = player.Callback(choicesAvailableToPlayer);
if (playerChose is DrawChoiceType)
{
    //Do whatever needs to be done in the game engine for this selection.
}
else if (playerChose is AllocatePointsChoiceType)
{
     //Do whatever needs to be done in the game engine for this selection.
}

Não sei ao certo como o seu código da interface do usuário está exibindo essas opções de escolha para o usuário, mas seria muito simples direcionar a interface do usuário dessa estrutura.

    
por 10.11.2015 / 09:45
fonte