Skip to content

Commit

Permalink
Expose resets and remove scene monitoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Claytonious committed Aug 11, 2014
1 parent 77c4669 commit 5b133fa
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 112 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ public class MessageHandler
public Type MessageType { get; set; }
public object Target { get; set; }
public Delegate Delegate { get; set; }
public HandlerPersistence Persistence { get; set; }
}
}
57 changes: 15 additions & 42 deletions FrictionlessUnity/Assets/Frictionless/Scripts/MessageRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,17 @@

namespace Frictionless
{
public class MessageRouter : MonoBehaviour
public class MessageRouter
{
private Dictionary<Type,List<MessageHandler>> handlers = new Dictionary<Type, List<MessageHandler>>();
private List<Delegate> pendingRemovals = new List<Delegate>();
private bool isRaisingMessage;
private int previousLevel;

void Start()
public MessageRouter()
{
previousLevel = Application.loadedLevel;
}

void Update()
{
if (Application.loadedLevel != previousLevel)
{
List<Type> obsoleteTypes = new List<Type>();
foreach(var handlerList in handlers.Values)
{
foreach(var handler in new List<MessageHandler>(handlerList))
{
if (handler.Persistence == HandlerPersistence.DieWithScene)
{
handlerList.Remove(handler);
if (handlerList.Count == 0)
obsoleteTypes.Add (handler.MessageType);
}
}
}

foreach(var t in obsoleteTypes)
{
handlers.Remove(t);
}

previousLevel = Application.loadedLevel;
}
}

public void AddHandler<T>(Action<T> handler, HandlerPersistence persistence = HandlerPersistence.DieWithScene)
public void AddHandler<T>(Action<T> handler)
{
List<MessageHandler> delegates = null;
if (!handlers.TryGetValue(typeof(T), out delegates))
Expand All @@ -52,15 +23,7 @@ public void AddHandler<T>(Action<T> handler, HandlerPersistence persistence = Ha
handlers[typeof(T)] = delegates;
}
if (delegates.Find(x => x.Delegate == handler) == null)
{
delegates.Add(new MessageHandler()
{
Target = handler.Target,
Delegate = handler,
MessageType = typeof(T),
Persistence = persistence
});
}
delegates.Add(new MessageHandler() { Target = handler.Target, Delegate = handler });
}

public void RemoveHandler<T>(Action<T> handler)
Expand All @@ -81,7 +44,6 @@ public void RemoveHandler<T>(Action<T> handler)

public void RaiseMessage(object msg)
{
Debug.Log ("Raising " + msg);
List<MessageHandler> delegates = null;
if (handlers.TryGetValue(msg.GetType(), out delegates))
{
Expand All @@ -104,5 +66,16 @@ public void RaiseMessage(object msg)
pendingRemovals.Clear();
}
}

public void Reset()
{
handlers.Clear();
}

public class MessageHandler
{
public object Target { get; set; }
public Delegate Delegate { get; set; }
}
}
}
107 changes: 57 additions & 50 deletions FrictionlessUnity/Assets/Frictionless/Scripts/ServiceFactory.cs
Original file line number Diff line number Diff line change
@@ -1,68 +1,75 @@
using System;
using System.Collections.Generic;

/// <summary>
/// A simple, *single-threaded*, dependency injection container appropriate for use with Unity.
/// </summary>
using UnityEngine;

namespace Frictionless
public class ServiceFactory
{
/// <summary>
/// A simple, *single-threaded*, dependency injection container appropriate for use with Unity.
/// </summary>
public static class ServiceFactory
private static Dictionary<Type,Type> singletons = new Dictionary<Type, Type>();
private static Dictionary<Type,Type> transients = new Dictionary<Type, Type>();
private static Dictionary<Type,object> singletonInstances = new Dictionary<Type, object>();

private ServiceFactory()
{
private static Dictionary<Type,Type> singletons = new Dictionary<Type, Type>();
private static Dictionary<Type,Type> transients = new Dictionary<Type, Type>();
private static Dictionary<Type,object> singletonInstances = new Dictionary<Type, object>();
}

public static void RegisterSingleton<TConcrete>()
{
singletons[typeof(TConcrete)] = typeof(TConcrete);
}
public static void Reset()
{
singletons.Clear();
transients.Clear();
singletonInstances.Clear();
}

public static void RegisterSingleton<TAbstract,TConcrete>()
{
singletons[typeof(TAbstract)] = typeof(TConcrete);
}

public static void RegisterSingleton<TConcrete>(TConcrete instance)
{
singletons[typeof(TConcrete)] = typeof(TConcrete);
singletonInstances[typeof(TConcrete)] = instance;
}
public static void RegisterSingleton<TConcrete>()
{
singletons[typeof(TConcrete)] = typeof(TConcrete);
}

public static void RegisterTransient<TAbstract,TConcrete>()
{
transients[typeof(TAbstract)] = typeof(TConcrete);
}
public static void RegisterSingleton<TAbstract,TConcrete>()
{
singletons[typeof(TAbstract)] = typeof(TConcrete);
}

public static void RegisterSingleton<TConcrete>(TConcrete instance)
{
singletons[typeof(TConcrete)] = typeof(TConcrete);
singletonInstances[typeof(TConcrete)] = instance;
}

public static T Resolve<T>() where T : class
public static void RegisterTransient<TAbstract,TConcrete>()
{
transients[typeof(TAbstract)] = typeof(TConcrete);
}

public static T Resolve<T>() where T : class
{
T result = default(T);
Type concreteType = null;
if (singletons.TryGetValue(typeof(T), out concreteType))
{
T result = default(T);
Type concreteType = null;
if (singletons.TryGetValue(typeof(T), out concreteType))
object r = null;
if (!singletonInstances.TryGetValue(typeof(T), out r))
{
object r = null;
if (!singletonInstances.TryGetValue(typeof(T), out r))
if (concreteType.IsSubclassOf(typeof(MonoBehaviour)))
{
if (concreteType.IsSubclassOf(typeof(MonoBehaviour)))
{
GameObject singletonGameObject = new GameObject();
r = singletonGameObject.AddComponent(concreteType);
singletonGameObject.name = typeof(T).ToString() + " (singleton)";
}
else
r = concreteType.GetConstructor(Type.EmptyTypes).Invoke(new object[] { });
singletonInstances[typeof(T)] = r;
GameObject singletonGameObject = new GameObject();
r = singletonGameObject.AddComponent(concreteType);
singletonGameObject.name = typeof(T).ToString() + " (singleton)";
}
result = (T)r;
}
else if (transients.TryGetValue(typeof(T), out concreteType))
{
object r = concreteType.GetConstructor(Type.EmptyTypes).Invoke(new object[] { });
result = (T)r;
else
r = concreteType.GetConstructor(Type.EmptyTypes).Invoke(new object[] { });
singletonInstances[typeof(T)] = r;
}
else
UnityEngine.Debug.LogError("Failed to resolve injected type for [" + typeof(T) + "] - it needs to be registered before being used!");
return result;
result = (T)r;
}
else if (transients.TryGetValue(typeof(T), out concreteType))
{
object r = concreteType.GetConstructor(Type.EmptyTypes).Invoke(new object[] { });
result = (T)r;
}
return result;
}
}
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,20 @@ void Update()

In real projects, it's typical to reuse a few message instances instead of `new`ing them each time you raise them (just change their properties and raise again). Regular GC best practices apply here.

#Changing Scenes and Unloading
If you change scenes in your game, then you need to clear your message handlers and service registrations to prevent accidentally carrying references to dead objects across to the new scene. Doing this is simple:

```c#
// First clear all message handlers and service registrations
ServiceFactory.Resolve<MessageRouter>().Reset();
ServiceFactory.Reset();

// Then load your new scene
Application.LoadLevel("foo");
```

If you actually *want* message handlers to live across scene loads, then just use `RemoveHandler` for all of the ones that should be removed, and don't call `Reset`. This requires a certain amount of discipline - you have to remember to `RemoveHandler` for every `AddHandler` that shouldn't survive into the new scene, but it's easy enought to do.

#MVVM and Higher Architecture
With `ServiceFactory` and `MessageRouter`, you have all of the building blocks that you need to implement MVVM or other patterns that separate view from logic. Frictionless itself doesn't care - you're free to go full MVVM or to apply a smaller subset of that pattern to only those places where you feel it adds genuine value.

Expand Down

0 comments on commit 5b133fa

Please sign in to comment.