Flyweight デザイン パターンの実装

# RPG ゲームにマップを実装する

Flyweight は構造設計パターンの 1 つです。類似のオブジェクトとできるだけ多くのデータを共有することにより、使用されるメモリの量を減らすために使用されます。このドキュメントでは、Flyweight DP を適切に使用する方法を説明します。

簡単な例でその考え方を説明しましょう。 RPG ゲームに取り組んでいて、いくつかのキャラクターを含む巨大なファイルをロードする必要があると想像してください。例:

  • # 草です。その上を歩くことができます。
  • $ 出発点です
  • @ ロックです。その上を歩くことはできません。
  • % 宝箱です

地図のサンプル:

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

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

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

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

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

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

これらのオブジェクトは類似した特性を持っているため、マップ フィールドごとに個別のオブジェクトを作成する必要はありません。フライウェイトの使い方を紹介します。

フィールドが実装するインターフェイスを定義しましょう:

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

これで、フィールドを表すクラスを作成できます。また、どうにかしてそれらを特定する必要があります (私は列挙を使用しました):

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; } }
}

前述したように、フィールドごとに個別のインスタンスを作成する必要はありません。 リポジトリを作成する必要があります フィールドの。 Flyweight DP の本質は、必要な場合にのみオブジェクトを動的に作成し、それがまだレポに存在しない場合にのみオブジェクトを作成するか、既に存在する場合はそれを返すことです。これを処理する簡単なクラスを書きましょう:

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);
    }
}

すごい!これでコードをテストできます:

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);       
    }
}

コンソールの結果は次のようになります:

Grass の新しいインスタンスを作成しましたRock の新しいインスタンスを作成しました

しかし、草を2回取得したいのに、なぜ草は1回しか表示されないのでしょうか?これは、初めて GetField を呼び出すためです 草のインスタンスは リポジトリ に存在しません 、作成されましたが、次に草が必要になったときに草が既に存在するため、それを返すだけです.