Skip to content

Commit 4f4a159

Browse files
committed
Adding MessageChannel - a pub-sub message-passing pattern
also adding DIScope extensions - this messaging system is particularly useful with DI and the extension provided allows to easily bind all the necessary interfaces to the DI Scope for easy retrieval of either publisher or subscriber interfaces to the message channel
1 parent 9ee082e commit 4f4a159

File tree

4 files changed

+115
-0
lines changed

4 files changed

+115
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using UnityEngine.Assertions;
4+
5+
namespace BossRoom.Scripts.Shared.Infrastructure
6+
{
7+
public interface IPublisher<T>
8+
{
9+
void Publish(T message);
10+
}
11+
12+
public interface ISubscriber<T>
13+
{
14+
IDisposable Subscribe(Action<T> handler);
15+
}
16+
17+
public class MessageChannel<T> : IDisposable, IPublisher<T>, ISubscriber<T>
18+
{
19+
private readonly List<Action<T>> m_MessageHandlers = new List<Action<T>>();
20+
21+
private readonly Queue<Action> m_PendingHandlers = new Queue<Action>();
22+
private bool m_IsDisposed;
23+
24+
public void Dispose()
25+
{
26+
if (!m_IsDisposed)
27+
{
28+
m_IsDisposed = true;
29+
m_MessageHandlers.Clear();
30+
m_PendingHandlers.Clear();
31+
}
32+
}
33+
34+
public void Publish(T message)
35+
{
36+
while (m_PendingHandlers.Count > 0) m_PendingHandlers.Dequeue()?.Invoke();
37+
38+
foreach (var messageHandler in m_MessageHandlers) messageHandler?.Invoke(message);
39+
}
40+
41+
public IDisposable Subscribe(Action<T> handler)
42+
{
43+
Assert.IsTrue(!m_MessageHandlers.Contains(handler), "Attempting to subscribe with the same handler more than once");
44+
m_PendingHandlers.Enqueue(() => { DoSubscribe(handler); });
45+
var subscription = new Subscription(this, handler);
46+
return subscription;
47+
48+
void DoSubscribe(Action<T> _h)
49+
{
50+
if (_h != null && !m_MessageHandlers.Contains(_h))
51+
m_MessageHandlers.Add(_h);
52+
}
53+
}
54+
55+
private void Unsubscribe(Action<T> handler)
56+
{
57+
m_PendingHandlers.Enqueue(() => { DoUnsubscribe(handler); });
58+
59+
void DoUnsubscribe(Action<T> _h)
60+
{
61+
m_MessageHandlers.Remove(_h);
62+
}
63+
}
64+
65+
private class Subscription : IDisposable
66+
{
67+
private Action<T> m_Handler;
68+
private bool m_isDisposed;
69+
private MessageChannel<T> m_MessageChannel;
70+
71+
public Subscription(MessageChannel<T> messageChannel, Action<T> handler)
72+
{
73+
m_MessageChannel = messageChannel;
74+
m_Handler = handler;
75+
}
76+
77+
public void Dispose()
78+
{
79+
if (!m_isDisposed)
80+
{
81+
m_isDisposed = true;
82+
83+
if (!m_MessageChannel.m_IsDisposed) m_MessageChannel.Unsubscribe(m_Handler);
84+
85+
m_Handler = null;
86+
m_MessageChannel = null;
87+
}
88+
}
89+
}
90+
}
91+
}

Assets/BossRoom/Scripts/Shared/Infrastructure/MessageChannel.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace BossRoom.Scripts.Shared.Infrastructure
2+
{
3+
public static class MessageChannelDIExtenstions
4+
{
5+
public static void BindMessageChannel<TMessage>(this DIScope scope)
6+
{
7+
scope.BindAsSingle< MessageChannel<TMessage>, IPublisher<TMessage>, ISubscriber<TMessage>>();
8+
}
9+
}
10+
}

Assets/BossRoom/Scripts/Shared/Infrastructure/MessageChannelDIExtenstions.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)