Skip to content

Instantly share code, notes, and snippets.

@fadookie
Last active January 19, 2018 21:57
Show Gist options
  • Save fadookie/96a5967e55b3df3379d71c2f519c38c0 to your computer and use it in GitHub Desktop.
Save fadookie/96a5967e55b3df3379d71c2f519c38c0 to your computer and use it in GitHub Desktop.
Service manager for unity (for accessing single instances of objects globally) and tests
using System;
using System.Collections.Generic;
/// <summary>
/// Simple service manager. Allows global access to a single instance of any class.
/// Copyright (c) 2014-2017 Eliot Lash
/// </summary>
public class Services
{
//Statics
private static Services _instance;
//Instance
private Dictionary<Type, object> services = new Dictionary<Type, object>();
private Services() {
if (_instance != null) {
UnityEngine.Debug.LogError("Cannot have two instances of singleton.");
}
}
/// <summary>
/// Getter for singelton instance.
/// </summary>
public static Services instance {
get {
if (_instance == null) {
_instance = new Services();
}
return _instance;
}
}
/// <summary>
/// Set the specified service instance. Usually called like Set<ExampleService>(this).
/// </summary>
/// <param name="service">Service instance object.</param>
/// <typeparam name="T">Type of the instance object.</typeparam>
public void Set<T>(T service) where T : class {
services.Add(typeof(T), service);
}
/// <summary>
/// Removes the specified service.
/// </summary>
/// <typeparam name="T">Type of the instance object.</typeparam>
public void Remove<T>() where T : class {
services.Remove(typeof(T));
}
/// <summary>
/// Removes the specified service instance.
/// In cases such as where a MonoBehaviour is cleaning itself up in OnDestroy but this call is delayed
/// for several frames, using this method will ensure that a newer instance of the service is not removed
/// by the older instance being destroyed.
/// </summary>
/// <param name="service">Service instance object.</param>
/// <typeparam name="T">Type of the instance object.</typeparam>
public void Remove<T>(T service) where T : class {
if (services.ContainsValue(service)) {
services.Remove(typeof(T));
}
}
/// <summary>
/// Gets the specified service instance. Called like Get<ExampleService>().
/// </summary>
/// <typeparam name="T">Type of the service.</typeparam>
/// <returns>Service instance, or null if not initialized</returns>
public T Get<T>() where T : class {
T ret = null;
try {
ret = services[typeof(T)] as T;
} catch (KeyNotFoundException) {
}
return ret;
}
/// <summary>
/// Clears internal dictionary of service instances.
/// This will not clear out any global state that they contain,
/// unless there are no other references to the object.
/// </summary>
public void Clear() {
services.Clear();
}
}
using NUnit.Framework;
public class ServicesSpec
{
class MockService1 {}
class MockService2 {}
[SetUp]
public void SetUp() {
Services.instance.Clear();
}
public class Singleton : ServicesSpec
{
[Test]
public void ShouldReturnServiceInstance() {
Assert.That(Services.instance, Is.InstanceOf(typeof(Services)));
}
[Test]
public void ShouldReturnSameServiceInstanceOnMultipleCalls() {
var services = Services.instance;
var services2 = Services.instance;
Assert.That(services, Is.SameAs(services2));
}
[Test]
public void ShouldReturnSameServiceInstanceAfterClear() {
var services = Services.instance;
services.Clear();
var services2 = Services.instance;
Assert.That(services, Is.SameAs(services2));
}
}
public class SetAndGet : ServicesSpec
{
[Test]
public void ShouldSetAndGetService() {
var services = Services.instance;
var mockService = new MockService1();
services.Set<MockService1>(mockService);
Assert.That(services.Get<MockService1>(), Is.SameAs(mockService));
}
[Test]
public void ShouldThrowWhenSettingSameServiceTwice() {
var services = Services.instance;
services.Set<MockService1>(new MockService1());
Assert.That(
() => services.Set<MockService1>(new MockService1()),
Throws.ArgumentException
);
}
}
public class Clear : ServicesSpec
{
[Test]
public void ShouldRemoveAllServices() {
var services = Services.instance;
services.Set<MockService1>(new MockService1());
services.Set<MockService2>(new MockService2());
services.Clear();
Assert.That(services.Get<MockService1>(), Is.Null);
Assert.That(services.Get<MockService2>(), Is.Null);
}
}
public class Remove : ServicesSpec
{
[Test]
public void ShouldRemoveType() {
var services = Services.instance;
services.Set<MockService1>(new MockService1());
services.Set<MockService2>(new MockService2());
services.Remove<MockService1>();
Assert.That(services.Get<MockService1>(), Is.Null);
Assert.That(services.Get<MockService2>(), Is.InstanceOf(typeof(MockService2)));
}
}
public class RemoveInstance : ServicesSpec
{
[Test]
public void ShouldRemoveInstanceType() {
var services = Services.instance;
var firstInstance = new MockService1();
services.Set<MockService1>(firstInstance);
services.Remove<MockService1>();
services.Set<MockService1>(new MockService1());
services.Remove<MockService1>(firstInstance);
Assert.That(services.Get<MockService1>(), Is.InstanceOf(typeof(MockService1)));
Assert.That(services.Get<MockService1>(), Is.Not.SameAs(firstInstance));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment