Last active
January 19, 2018 21:57
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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