Nils
Nils

Reputation: 789

How can I abstract implementations away from monobehaviours?

I'm trying to create my first bigger project in Unity and I am struggling to understand how abstract implementations away from the monobehaviour classes attached to gameobjects.
I am very familiar with dependency injection (using ASP.NET) but it seems DI is not a great idea to include in Unity projects according to a number of different articles. These articles say Unity has to handle Inversion of Control already, which is the Service Locator principal. I cannot find any built in implementation aside from methods like GameObject#Find or GameObject#GetComponent, etc.

An example would be a class handling interaction with files:

public interface IFileHandler { }
public class FileHandler : IFileHandler { }

public class FileHandling : MonoBehaviour 
{
    private IFileHandler fileHandler;

    private void Awake()
    {
         this.fileHandler = new FileHandler();
    }
}

Now the constructor of FileHandler changes. I would have to change it in every class.
How can I decouple the FileHandler from FileHandling?

Thanks in advance

Upvotes: 0

Views: 212

Answers (1)

Pasi Österman
Pasi Österman

Reputation: 2177

I recall that in Asp.net there's Program.cs with main method that boots everything up. In unity you could have GameObject with similar Program.cs script that has been assigned a very low Script Execution Order. The script simply initialises your game systems if they have not been initialised yet.

During initialization you can create system instances and store to a static class like toolbox from where you can later reference them from anywhere. If your system needs to listen to unity events like update you can create a new GameObject with the script and set DontDestroyOnLoad enabled and store it to the Toolbox as well.

Toolbox is basically singleton for storing your other "singletons" so they don't have to be singletons. If you use interfaces you can easily swap out the implementation for the SaveSystem for example.

// Enum to allow multiple instances if needed
enum GameSystems { SaveSystem, GameManager }

public class GameTB {

    //usage GameTB.toolBox.SetTool(GameSystems.SaveSystem, new SaveSystem());
    public static ToolBox<GameSystems> toolBox = new ToolBox<GameSystems>();
    
    //usage GameTB.SaveSystem.SaveGame();
    public static ISaveSystem SaveSystem 
    { 
        get
        { 
            return toolBox.GetTool<ISaveSystem>(GameSystems.SaveSystem); 
        }
    }
    
    public static IGameManager GameManager 
    { 
        get
        { 
            return toolBox.GetTool<IGameManager>(GameSystems.GameManager); 
        }
    }
}
public class ToolBox<T>
{
    private Dictionary<T, object> Tools { get; } = new Dictionary<T, object>();

    public K GetTool<K>(T key)
    {
        if (Tools.ContainsKey(key))
            return (K)Tools[key];
        else
            return default(K);
    }

    public void SetTool(T key, object tool)
    {
        if (!Tools.ContainsKey(key))
            Tools[key] = tool;
        else
            Tools.Add(key, tool);
    }

    public bool ContainsTool(T key)
    {
        if (Tools.ContainsKey(key) && Tools[key] != null)
            return true;
        else
            return false;
    }

    public void ClearTools()
    {
        Tools.Clear();
    }
}

Upvotes: 1

Related Questions