La classe A richiede informazioni aggiuntive quando lo si utilizza. Come passare a B?

2

Nota : sono consapevole della scarsa formulazione del titolo, ma non sono riuscito a trovare uno migliore. Se qualcuno ha un'idea migliore, modifica il titolo.

Sto provando a ricreare un gioco di carte, ma ho difficoltà a risolvere un problema. Ogni carta nel gioco ha un color (enum) e rank (enum). Alcune carte hanno alcune abilità speciali che richiedono dati aggiuntivi. Queste carte hanno sempre un colore speciale dato solo alle carte con abilità speciali. Il Game ha uno stack a cui vengono aggiunte carte giocate tramite un metodo void playCard(Card) .

Un esempio di abilità speciale sta cambiando il colore della carta in cima alla pila. Il nuovo colore è totalmente a discrezione del giocatore. Il problema è che questo nuovo colore deve essere deciso quando viene giocata la carta e prima che il colore della carta speciale sia solo SPECIAL . Ma come faccio a passare i nuovi dati di colore in un modo carino (vale a dire, quindi non ho bisogno di usare instanceof ed estrarre i dati, ma ottenerli come card.getColor() )? Idealmente mi piacerebbe che fosse una carta speciale prima e dopo averla giocata per camuffarsi come una normale carta (o farla sembrare come). Se possibile, vorrei comunque poter recuperare la carta speciale originale.

Ho pensato di risolvere questo creando un FakeCard e aggiungendo un metodo a SpecialCard per generare FakeCard e passare i dati richiesti a quel metodo, ma temo un accoppiamento aggiuntivo che si verificherebbe come speciale differente le carte richiedono diversi dati aggiuntivi e non vogliono violare il principio Open / Closed (usare questo approccio mi richiederebbe di farlo). Non riesco a pensare a nient'altro però. Hai qualche suggerimento su come affrontare questo problema?

    
posta Mibac 08.08.2017 - 22:52
fonte

9 risposte

2

Non sono uno sviluppatore java, ma un giocatore di vecchia data Magic The Gathering , quindi comprendo Infatti, alcuni giochi possono avere carte che fanno molte e pazzesche cose diverse.

Hai pensato di cambiare il modo in cui rappresenti le relazioni tra il gioco, la pila di carte e le carte? Attualmente penso che tu abbia un gioco che ha un mazzo di carte e una pila di carte. E hai nel metodo Game a playCard che suona Card , perché pensi che Game o (un giocatore) giochi una carta.

Ma invece del Gioco che gioca le carte, pensa alle carte che cambiano il gioco. Che cosa succede se hai una classe base Card implementata dalle tue carte e hai un metodo Card::play che prende un GameState e il suo stack di carte come argomento? In questo modo ogni carta ha la libertà di manipolare e modificare lo stato del gioco. Le tue carte possono aggiungersi allo stack e decidere i propri effetti. Se la carta non è speciale, verrà riprodotta normalmente.

Ora il colore che l'utente vuole dare alla carta speciale deve essere passato da qualche parte, quindi devi sottoclasse Card con SpecialCard in modo da poter passare quell'argomento aggiuntivo. Dovresti aggiungere il metodo SpecialCard::play(GameState game, Color color) (che sarebbe unittest-friendly a causa dell'iniezione di dipendenza).

Saresti quindi in grado di chiamare Card::play(GameState game) che chiamerebbe SpecialCard::play(GameState game) dove chiedi all'utente il colore che desidera utilizzare o puoi chiamare i tuoi test.

Per una carta speciale, una volta che l'input dell'utente è stato recuperato, SpecialCard::play(GameState game) chiamerebbe SpecialCard::play(GameState game, Color color) con le informazioni giuste.

La condivisione degli stessi interni evita la duplicazione del codice ed è utile anche per il test.

    
risposta data 15.01.2018 - 19:12
fonte
0

Se l'abilità speciale può essere "qualsiasi cosa" e può richiedere input arbitrari dal giocatore quando viene giocata, allora ogni tipo di SpecialCard deve effettivamente avere il proprio codice UI per ottenere quell'input dal giocatore. Quindi dai il metodo SpecialCard an invokeAbility() che è chiamato da playCard() e o hai sottoclassi di SpecialCard (o dagli un campo SpecialAbility ) per implementare le diverse abilità.

    
risposta data 08.08.2017 - 23:06
fonte
0

Sembra un problema che può essere risolto usando il polimorfismo / ereditarietà. Non ho fatto Java in un po 'e la mia sintassi probabilmente non è valida, ma penso di poter ottenere il mio punto di vista. Non sono sicuro di cosa ti impedisca di fare quanto segue

// All classes that implement ICard is guaranteed to have the following private fields and public methods.
interface ICard {
   private Color color;
   private Rank rank;

   public Color getColor();
   public Rank getRank();
   public void invokeAbility();
}

public class Card implements ICard {
   public Card(Color color){
      this.color = color;
   }
   // ... implement getColor and getRank and invokeAbility
}

// SpecialCard is an instance of Card so it can be used in the same manner as Card
public class SpecialCard extends Card {

   // overload the constructor
   public SpecialCard(){

     // Special card instantiated w/o Color enum is defaulted to Color.SPECIAL
     super(Color.SPECIAL);
   }

   public SpecialCard(Color color){

     super(color);
   }

   // Override invokeAbility
   public invokeAbility(){
     // ....
   }
}
    
risposta data 08.08.2017 - 23:31
fonte
0

Con quello che ho capito dalla domanda creerei una ColorChangingCard' class that extends the base Card 'con alcuni metodi che la rendono speciale. Questi metodi chiamano i metodi sulla classe genitore per cambiare lo stato della carta.

    
risposta data 08.08.2017 - 23:35
fonte
0

Questo è approssimativo e i dettagli se vuoi avere un colore SPECIALE o no possono essere cambiati ma dovrebbe darti l'idea di base:

public interface Card
{
    public enum Color {
        YELLOW, RED, GREEN, BLUE, SPECIAL
    }

    boolean canPlaceOn(Card other);

    Color color();

    default Color playedColor()
    {
        return color();
    }

    Rank rank();

    public void assign(Player player);

    Player player();
}

public class BasicCard extends AbstractCard
{
    private final Rank rank;
    private final Color color;

    BasicCard(Rank rank, Color color)
    {
        this.rank = rank;
        this.color = color;
    }

    @Override
    public boolean canPlaceOn(Card other)
    {
        return other.color() == this.color() || this.rank() == other.rank();
    }

    @Override
    public Color color()
    {
        return color;
    }

    @Override
    public Rank rank()
    {
        return rank;
    }
}

public class SpecialCard extends AbstractCard
{
    private Color color = Color.SPECIAL;

    @Override
    public boolean canPlaceOn(Card other)
    {
        // modify as needed
        return true;
    }

    @Override
    public Color Color()
    {
       return Color.SPECIAL;
    }

    @Override
    public Color playedColor()
    {
       while (color == Color.SPECIAL) {
           color = UserInterface.selectColor(player());
       }

       return color;
    }

    @Override
    public Rank rank()
    {
        // not sure what goes here
        return null;
    }
}
    
risposta data 09.08.2017 - 17:18
fonte
0
 > Class A requires additional info when using it. How to pass it to B?

La tua domanda iniziale era: come dire al PlayEngine "B" che la carta "A" è qualcosa di speciale?

Se segui tell, non chiedi pattern impari che il giocatore "X" dice alla speciale "A" quale colore usare e la speciale "A" dice a PlayEngine "B" quale colore usare.

Hai bisogno di due diversi tipi di carta:

  • la maggior parte delle carte sono di tipo "Scheda in cui il colore non può essere cambiato"
  • e ci sono alcuni "Scheda in cui il giocatore può dire alla carta quale colore deve avere in fase di esecuzione"

Entrambi i tipi di carte hanno un metodo play() che dice al PlayEngine quale colore è prossimo

Se la tua StandardCard ha anche un metodo setColor() (che non consiglio), l'implementazione di it-s dovrebbe generare PlayerIsCheetingException :-))

    
risposta data 09.08.2017 - 17:53
fonte
0

Prova un visitatore con 2 metodi con lo stesso nome, ma uno che prende la RegularCard come parametro e l'altra con la SpecialCard.

Quindi puoi definire un metodo in BaseCard che prenderà il visitatore e invocherà con se stesso la risoluzione del sottotipo.

Qualcosa del genere:

class CardDealer {
    void play(RegularCard card) {
        ...
    }
    void play(SpecialCard specialCard) {
        ...
    }
}
abstract class Card {
    draw(CardDealer visitor) {
        visitor.play(this);
    }
}

o

interface Playable {
    void play(CardDealer dealer);
}

e sovrascrivi il metodo di conseguenza, in questo caso puoi usare nomi diversi per i metodi dei visitatori

    
risposta data 09.08.2017 - 23:19
fonte
0

Il tuo problema nella modellazione deriva dal fatto che getColor() è a doppio uso:

  1. Determinare se è possibile giocare la carta, cioè seguire la causa. bool canPlay(Suit) sarebbe più appropriato.
  2. Determinare quale seme deve essere seguito dalla carta successiva. Suit getSuit() sarebbe appropriato.

Quindi, hai bisogno di due funzioni, una che restituisce il suo colore riprodotto, Suit getSuit() e una che determina se può essere riprodotta canPlay(Suit) .

Le carte speciali richiedono inoltre una funzione per cambiare il colore, setSuit() .

A seconda di come decidi di giocare, hai bisogno di almeno due classi, RegularCard e SpecialCard . O implementano / ereditano da un'interfaccia / base comune, oppure quest'ultima estende la prima. Entrambe le soluzioni sono valide.

    
risposta data 10.08.2017 - 00:57
fonte
0

La carta non cambia colore. Il colore indicato quando la carta è giocata fa parte dello stato del gioco, insieme, ad esempio, alla direzione del turno (se si implementa una carta "reverse").

Potresti trovare più semplice implementare il gioco in un modo guidato dai test, invece di cercare di creare un progetto di classe iniziale. L'input per ogni test sarebbe lo stato del gioco e una possibile carta da giocare, e l'output sarebbe se è legale giocare quella carta e lo stato del gioco risultante. Quindi puoi testare e implementare una regola alla volta. Calcola le classi man mano che diventa conveniente.

    
risposta data 15.01.2018 - 19:32
fonte

Leggi altre domande sui tag