Implementazione del modello di progettazione dei pesi mosca

Implementazione del modello di progettazione dei pesi mosca

# Implementazione della mappa nel gioco di ruolo

Il peso mosca è uno dei modelli di progettazione strutturale. Viene utilizzato per ridurre la quantità di memoria utilizzata condividendo quanti più dati possibile con oggetti simili. Questo documento ti insegnerà come usare correttamente Flyweight DP.

Lascia che te lo spieghi l'idea con un semplice esempio. Immagina di lavorare su un gioco di ruolo e di dover caricare un file enorme che contiene alcuni personaggi. Ad esempio:

  • # è erba. Puoi camminarci sopra.
  • $ è il punto di partenza
  • @ è roccia. Non puoi camminarci sopra.
  • % è uno scrigno del tesoro

Esempio di mappa:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@############@@@@@######@#$@@@

@#############@@@######@###@@@

@#######%######@###########@@@

@############################@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Poiché questi oggetti hanno caratteristiche simili, non è necessario creare oggetti separati per ogni campo della mappa. Ti mostrerò come usare i pesi mosca.

Definiamo un'interfaccia che i nostri campi implementeranno:

public interface IField
{
    string Name { get; }
    char Mark { get; }
    bool CanWalk { get; }
    FieldType Type { get; }
}

Ora possiamo creare classi che rappresentano i nostri campi. Dobbiamo anche identificarli in qualche modo (ho usato un'enumerazione):

public enum FieldType
{
    GRASS,
    ROCK,
    START,
    CHEST
}
public class Grass : IField
{
    public string Name { get { return "Grass"; } }
    public char Mark { get { return '#'; } }
    public bool CanWalk { get { return true; } }
    public FieldType Type { get { return FieldType.GRASS; } }
}
public class StartingPoint : IField
{
    public string Name { get { return "Starting Point"; } }
    public char Mark { get { return '$'; } }
    public bool CanWalk { get { return true; } }
    public FieldType Type { get { return FieldType.START; } }
}
public class Rock : IField
{
    public string Name { get { return "Rock"; } }
    public char Mark { get { return '@'; } }
    public bool CanWalk { get { return false; } }
    public FieldType Type { get { return FieldType.ROCK; } }
}
public class TreasureChest : IField
{
    public string Name { get { return "Treasure Chest"; } }
    public char Mark { get { return '%'; } }
    public bool CanWalk { get { return true; } } // you can approach it
    public FieldType Type { get { return FieldType.CHEST; } }
}

Come ho detto, non è necessario creare un'istanza separata per ogni campo. Dobbiamo creare un repository di campi. L'essenza di Flyweight DP è che creiamo dinamicamente un oggetto solo se ne abbiamo bisogno e non esiste ancora nel nostro repository, o lo restituiamo se esiste già. Scriviamo una classe semplice che gestirà questo per noi:

public class FieldRepository
{
    private List<IField> lstFields = new List<IField>();

    private IField AddField(FieldType type)
    {
        IField f;
        switch(type)
        {
            case FieldType.GRASS: f = new Grass(); break;
            case FieldType.ROCK: f = new Rock(); break;
            case FieldType.START: f = new StartingPoint(); break;
            case FieldType.CHEST:
            default: f = new TreasureChest(); break;
        }
        lstFields.Add(f); //add it to repository
        Console.WriteLine("Created new instance of {0}", f.Name);
        return f;
    }
    public IField GetField(FieldType type)
    {
        IField f = lstFields.Find(x => x.Type == type);
        if (f != null) return f;
        else return AddField(type);
    }
}

Grande! Ora possiamo testare il nostro codice:

public class Program
{
    public static void Main(string[] args)
    {
        FieldRepository f = new FieldRepository();
        IField grass = f.GetField(FieldType.GRASS);
        grass = f.GetField(FieldType.ROCK);
        grass = f.GetField(FieldType.GRASS);       
    }
}

Il risultato nella console dovrebbe essere:

Creata una nuova istanza di GrassCreata una nuova istanza di Rock

Ma perché l'erba appare solo una volta se volessimo ottenerla due volte? Questo perché la prima volta chiamiamo GetField l'istanza grass non esiste nel nostro repository , quindi è stato creato, ma la prossima volta che avremo bisogno di erba esiste già, quindi lo restituiamo solo.