【C++】Flyweight(デザインパターン)

## Flyweightとは
**多数のインスタンスを共有することでオブジェクトの数を減らし、
メモリ効率の大幅な改善が見込まれ、メモリ使用量を削減できる**設計。

### C++
```C++:Flyweight
#include <iostream>
#include <map>
#include <string>
#include <vector>

/// <summary>
/// シングルトン
/// 
/// templateで汎用性強化
/// </summary>
template<typename T>
class Singleton
{
public:
	/// <summary>
	/// デフォルトコンストラクタ
	/// 
	/// コンストラクタは絶対に呼ばない!!
	/// </summary>
	Singleton() {}

	/// <summary>
	/// インスタンスを取得する
	/// </summary>
	static T* GetInstance(void)
	{
		if (instance == nullptr)
		{
			// 初回のみ動的確保
			instance = new T;
		}

		return instance;
	}

	/// <summary>
	/// インスタンスを破棄する
	/// </summary>
	static void DeleteInstance(void)
	{
		// 削除は動的確保している時のみ
		if (instance != nullptr)
		{
			delete instance;
			instance = nullptr;
		}
	}

private:
	static T* instance;
};
template<typename T>
T* Singleton<T>::instance = nullptr;

/// <summary>
/// フライウェイト
/// </summary>
class Flyweight
	: public Singleton<Flyweight>
{
	// friend : privateへアクセス可能にする
	friend class Singleton<Flyweight>;

public:
	enum class KIND_OF_ENEMY
	{
		SLIME,
		GOBlIN,
		DRAGON,
	};

	/// <summary>
	/// オブジェクト名を取得する
	/// </summary>
	/// <param name="key">取得するオブジェクトのキー</param>
	/// <returns>"\0" = ERROR、"\0"以外 = オブジェクト名</returns>
	std::string GetName(KIND_OF_ENEMY key)
	{
		// map.count(Key)->数
		if (namesEnemy.count(key) == 0)	return "\0"; // エラー

		return namesEnemy[key];
	}
	/// <summary>
	/// オブジェクト名を設定する
	/// </summary>
	/// <param name="key">設定するオブジェクトのキー</param>
	void SetName(KIND_OF_ENEMY key)
	{
		// map.count(Key)->数
		if (namesEnemy.count(key) == 1)	return;	// 既に存在します

		switch (key)
		{
		case Flyweight::KIND_OF_ENEMY::SLIME:
			namesEnemy[key] = "Slime";
			break;
		case Flyweight::KIND_OF_ENEMY::GOBlIN:
			namesEnemy[key] = "Goblin";
			break;
		case Flyweight::KIND_OF_ENEMY::DRAGON:
			namesEnemy[key] = "Dragon";
			break;
		default:
			namesEnemy[key] = "\0";
			break;
		}
	}

private:
	std::map<KIND_OF_ENEMY, std::string> namesEnemy;
};

/// <summary>
/// 敵の抽象クラス
/// </summary>
class Enemy
{
public:
	/// <summary>
	/// コンストラクタ
	/// </summary>
	Enemy()
	{
		name = "\0";
	}

	inline virtual void Output(void) = 0;

	std::string name;
};

/// <summary>
/// スライム
/// </summary>
class Slime
	: public Enemy
{
public:
	/// <summary>
	/// コンストラクタ
	/// </summary>
	Slime()
	{
		std::cout << "スライムが現れた!!" << std::endl;
		name = Flyweight::GetInstance()->GetName(Flyweight::KIND_OF_ENEMY::SLIME);
	}

	/// <summary>
	/// 出力
	/// </summary>
	inline void Output(void) override
	{
		std::cout << "オブジェクト名:" << name << std::endl;
	}
};

/// <summary>
/// ゴブリン
/// </summary>
class Goblin
	: public Enemy
{
public:
	/// <summary>
	/// コンストラクタ
	/// </summary>
	Goblin()
	{
		std::cout << "ゴブリンが現れた!!" << std::endl;
		name = Flyweight::GetInstance()->GetName(Flyweight::KIND_OF_ENEMY::GOBlIN);
	}

	/// <summary>
	/// 出力
	/// </summary>
	inline void Output(void) override
	{
		std::cout << "オブジェクト名:" << name << std::endl;
	}
};

/// <summary>
/// ドラゴン
/// </summary>
class Dragon
	: public Enemy
{
public:
	/// <summary>
	/// コンストラクタ
	/// </summary>
	Dragon()
	{
		std::cout << "ドラゴンが現れた!!" << std::endl;
		name = Flyweight::GetInstance()->GetName(Flyweight::KIND_OF_ENEMY::DRAGON);
	}

	/// <summary>
	/// 出力
	/// </summary>
	inline void Output(void) override
	{
		std::cout << "オブジェクト名:" << name << std::endl;
	}
};

int main(void)
{
	Flyweight::GetInstance()->SetName(Flyweight::KIND_OF_ENEMY::SLIME);
	Flyweight::GetInstance()->SetName(Flyweight::KIND_OF_ENEMY::GOBlIN);
	Flyweight::GetInstance()->SetName(Flyweight::KIND_OF_ENEMY::DRAGON);

	// 動的配列の作成
	std::vector<Enemy*> enemies =
	{
		new Slime(),
		new Slime(),
		new Goblin(),
		new Goblin(),
		new Goblin(),
		new Dragon(),
	};

	//敵の行動
	for (auto enemy : enemies)
	{
		enemy->Output();
	}

	// 動的配列の解放
	enemies.clear();
	enemies.shrink_to_fit();

	Flyweight::DeleteInstance();

	return 0;
}
```

### C#
```C#:Flyweight.cs
using System;
using System.Collections.Generic;
using System.Xml.Linq;

/// <summary>
/// シングルトン
/// 
/// ジェネリックで汎用性強化
/// where T : new()はインスタンス化の保証
/// </summary>
public class Singleton<T> where T : new()
{
    private static T instance;

    /// <summary>
	/// デフォルトコンストラクタ
	/// 
	/// private化で複製禁止
	/// </summary>
    public Singleton() { }

    /// <summary>
	/// インスタンスを取得する
	/// </summary>
    public static T GetInstance
    {
        get
        {
            // 初回のみ動的確保
            return instance ??= new T();
        }
    }
}

/// <summary>
/// フライウェイト
/// </summary>
public class Flyweight : Singleton<Flyweight>
{
    public enum KIND_OF_ENEMY
    {
        SLIME,
        GOBLIN,
        DRAGON,
    };
    private Dictionary<KIND_OF_ENEMY, string> namesEnemy = new Dictionary<KIND_OF_ENEMY, string>();

    /// <summary>
    /// オブジェクト名を取得する
    /// </summary>
    /// <param name="key">取得するオブジェクトのキー</param>
    /// <returns>"\0" = ERROR、"\0"以外 = オブジェクト名</returns>
    public string GetName(KIND_OF_ENEMY key)
    {
        // map[Key].Length
        if (!namesEnemy.ContainsKey(key) || namesEnemy[key].Length == 0) return "\0"; // エラー

        return namesEnemy[key];
    }

    /// <summary>
    /// オブジェクト名を設定する
    /// </summary>
    /// <param name="key">設定するオブジェクトのキー</param>
    public void SetName(KIND_OF_ENEMY key)
    {
        if (namesEnemy.ContainsKey(key))
        {
            // map[Key].Length
            if (namesEnemy[key].Length > 0)
            {
                return; // 既に存在します
            }
        }

        switch (key)
        {
            case KIND_OF_ENEMY.SLIME:
                namesEnemy[key] = "Slime";
                break;
            case KIND_OF_ENEMY.GOBLIN:
                namesEnemy[key] = "Goblin";
                break;
            case KIND_OF_ENEMY.DRAGON:
                namesEnemy[key] = "Dragon";
                break;
        }
    }
}

/// <summary>
/// 敵の抽象クラス
/// </summary>
public abstract class Enemy
{
    public string name;

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public Enemy(string _name)
    {
        name = _name;
    }

    public abstract void Output();
}

/// <summary>
/// スライム
/// </summary>
public class Slime : Enemy
{
    /// <summary>
    /// コンストラクタ
    /// </summary>
    public Slime() : base("Slime")
    {
        Console.WriteLine("スライムが現れた!!");
    }

    /// <summary>
    /// 出力
    /// </summary>
    public override void Output()
    {
        Console.WriteLine($"オブジェクト名:{name}");
    }
}

/// <summary>
/// ゴブリン
/// </summary>
public class Goblin : Enemy
{
    /// <summary>
    /// コンストラクタ
    /// </summary>
    public Goblin() : base("Goblin")
    {
        Console.WriteLine("ゴブリンが現れた!!");
    }

    /// <summary>
    /// 出力
    /// </summary>
    public override void Output()
    {
        Console.WriteLine($"オブジェクト名:{name}");
    }
}

/// <summary>
/// ドラゴン
/// </summary>
public class Dragon : Enemy
{
    /// <summary>
    /// コンストラクタ
    /// </summary>
    public Dragon() : base("Dragon")
    {
        Console.WriteLine("ドラゴンが現れた!!");
    }

    /// <summary>
    /// 出力
    /// </summary>
    public override void Output()
    {
        Console.WriteLine($"オブジェクト名:{name}");
    }
}

public class Program
{
    static void Main()
    {
        Flyweight.GetInstance.SetName(Flyweight.KIND_OF_ENEMY.SLIME);
        Flyweight.GetInstance.SetName(Flyweight.KIND_OF_ENEMY.GOBLIN);
        Flyweight.GetInstance.SetName(Flyweight.KIND_OF_ENEMY.SLIME);

        // 敵の生成
        List<Enemy> enemies = new List<Enemy>
        {
            new Slime(),
            new Slime(),
            new Goblin(),
            new Goblin(),
            new Goblin(),
            new Dragon()
        };

        // 敵の行動
        foreach (Enemy enemy in enemies)
        {
            enemy.Output();
        }
    }
}
```