Alternativa all'utilizzo di getter / setter in una classe semplice

2

Sto scrivendo un gioco di dragamine con curses . Per rappresentare ogni tessera, ho scritto una piccola classe Tile che contiene informazioni sul possibile stato di ciascuna piastrella (indipendentemente dal fatto che sia contrassegnata o meno, indipendentemente dal fatto che abbia o meno una bomba sotto di essa e se sia o meno "nascosta"). Questa è l'interfaccia finora:

class Tile {
    bool hidden;
    bool containsBomb;
    bool hasFlag;

public:
    Tile(bool startWithBomb, bool startHidden = true);

    bool isHidden() const;
    void makeHidden();
    void unhide();

    bool hasBomb() const;
    void placeBomb();
    void removeBomb();

    bool hasFlag() const;
    void placeFlag();
    void removeFlag();

};

Avrei potuto creare setter per ciascuna una singola funzione, ma penso che essere separati porterà a un codice più leggibile ( tile.setFlag(false) vs tile.removeFlag() )

Il mio problema è che questo può potenzialmente portare a "stati cattivi". Ad esempio, una tessera non visibile può contenere un flag.

Leggevo questo post e molti utenti " bash "usando getter / setter; con buone ragioni.

La mia domanda è: prima di andare più lontano nel progettare questa classe, c'è un modo migliore di impostarlo per evitare l'uso di getter / setter generici per ogni flag privato?

    
posta Carcigenicate 07.05.2015 - 21:27
fonte

2 risposte

3

Sì, avere esattamente una coppia getter / setter per bandiera privata è un odore di codice. Se non si fornisce un'interfaccia semplice per nascondere alcuni dettagli di implementazione, non si beneficia molto dell'utilizzo di una classe.

Il mio principale suggerimento concreto sarebbe quello di rappresentare lo stato interno in un modo che rende "cattivi stati" teoricamente impossibili. Ad esempio:

class Tile {
    enum DisplayState { DEFAULT, FLAGGED, KNOWN_EMPTY };
    DisplayState displayState;
    bool containsBomb;
    ...
};

Si noti che containsBomb può essere vero o falso in uno dei tre stati, quindi ha senso tenere separato quel membro. Sta a te decidere se cose come chiamare removeFlag () su una tessera KNOWN_EMPTY dovrebbero generare un'eccezione o semplicemente non fare nulla.

Ora il problema della grande immagine. Se la parte del codice che usa gli oggetti Tile ha davvero bisogno di tutti questi metodi, cioè pieno accesso e controllo dell'intero stato di Tile, allora non è chiaro in che modo trarrai vantaggio da Tile una classe (al contrario di una struct POD ) innanzitutto. Forse sarebbe meglio avere una classe Board con alcuni array 2D di booleani per rappresentare tutte le bandiere / bombe / etc, dal momento che quella classe sarebbe in grado di nascondere una grande quantità di logica di iterazione dai suoi utenti. Non posso dire con certezza se sarebbe un miglioramento senza leggere tutto il tuo codice, ma spero che questo aiuti.

    
risposta data 07.05.2015 - 21:49
fonte
2

My problem is, this can potentially lead to "bad states".

È vero il contrario: il setter ti consente di evitare "stati cattivi". Sei tu a definire il comportamento della macchina a stati, specificando cosa succede quando una variabile cambia.

An unhidden tile shouldn't be able to contain a flag for example.

Pertanto, chiamando unhide() devi impostare hasFlag su false e placeFlag() dovrebbe solo impostare hasFlag su true se hidden è true.

Non esiste un "cattivo stato" perché è necessario definire bene la macchina a stati e gestire ogni caso. Questi setter sono solo l'implementazione di quella macchina a stati.

Raggiungere un "cattivo stato" è un difetto nella funzione di trasferimento.

    
risposta data 07.05.2015 - 23:47
fonte

Leggi altre domande sui tag