Nel corso di molti anni, mi sono sempre ritrovato a riconsiderare questo progetto, quindi volevo ottenere un feedback sulla mia soluzione.
Problema:
Ho bisogno di una quantità limitata di oggetti = istanze da una classe e non voglio esporre l'opzione per crearne altri. Voglio anche un facile accesso a loro da qualsiasi luogo e operatori come uguali / non uguali.
Devono essere istanze di classe, in quanto devono avere metodi, quindi un enum non funziona.
(semplificato) Esempio:
Prendi in considerazione ad esempio un gioco simile ad un gioco di scacchi.
- Ci sono tre colori di tessere per il tabellone necessario, più chiaro e più scuro, e una tessera di dimensioni diverse dove vengono messi i pezzi presi. Inoltre,
- i pezzi hanno un colore, in genere bianco o nero; e io uso NoColor per i campi vuoti.
Entrambi gli esempi, ogni istanza dovrebbe sapere come disegnare e serializzare se stessa.
Opzione ovvia:
Se faccio un enum semplice, i valori non possono trasportare metodi, quindi finisco con i metodi Draw e Serialize in qualche altra classe, che sono fondamentalmente un brutto interruttore / caso.
La mia soluzione:
Mi piace
- proteggi il costruttore e,
- crea istanze statiche (pubbliche) all'interno della classe (ora C ++ 17 lo consente anche direttamente con
inline
).
'Ogni istanza viene creata con un const' tipo ' in esso (fondamentalmente un enum ), impostato tramite il costruttore.
Ovunque mi occorrano le informazioni, utilizzo il puntatore const per l'istanza statica . Ciò consente di _compare e assegnarli facilmente e consente anche di chiamare le loro funzioni membro secondo necessità.
Permette anche agli altri giochi di derivare dalla classe e aggiungere nuove istanze statiche, ad esempio, un gioco a tre giocatori può aggiungere un'istanza Red all'interno di una classe derivata. Dato che distribuisco i puntatori alla classe base ovunque, è polimorfico e il codice non deve gestire in alcun modo il nuovo colore speciale ...
class Color
{
protected:
Color(char c) : color{c} {}
public:
void Serialize(Archive& ar) const { ar << color; }
private:
const char color;
public: // the only instances ever to exists; handed around by pointer
inline const static Color NoColor{'X'}; // C++17 syntax, otherwise
inline const static Color White{'W'}; // separate line in .cpp needed
inline const static Color Black{'B'};
};
class Chess3Color : public Color
{
protected:
Chess3Color(char c) : Color{c} {}
public:
inline const static Chess3Color Red{'R'};
};
Esempio di utilizzo:
...
// create a White King
Piece* p = new WhiteKing{&Type::King, &Color::White, IDB_W_K_BMP};
...
if (p->IsColor(&Color::White)) { ... }
...
p->Serialize(ar);
...
class Piece
{
...
// Piece member function for serialization
void Serialize(Archive& ar)
{
type->Serialize(ar);
color->Serialize(ar);
}
...
// check if this piece is of a certain color
// note that pointer compare is good enough!
bool IsColor(const Color* c) { return color == c; }
...
}
Domanda:
Vado avanti e indietro nella mia mente tra questo è un odore di codice o una soluzione piuttosto buona e pulita. Quindi è un odore di codice?
In quale altro modo sarebbe meglio progettarlo?
Ho letto molto su Singletons, e non penso che mi farebbero del bene (a parte il fatto che alcune persone pensano che siano anche un buon odore di codice).