-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #169 from r-koubou/feature/event-managements
feature/event-managements
- Loading branch information
Showing
7 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using KSPCompiler.Domain.Events; | ||
|
||
using NUnit.Framework; | ||
|
||
namespace KSPCompiler.Domain.Tests.Events; | ||
|
||
[TestFixture] | ||
public class EventTest | ||
{ | ||
[Test] | ||
public void DispatchTest() | ||
{ | ||
var dispatcher = new EventDispatcher(); | ||
var observer = new MockIntEventObserver(); | ||
using var token = dispatcher.Subscribe( observer ); | ||
|
||
dispatcher.Dispatch( new MockIntEvent( 1 ) ); | ||
dispatcher.Dispatch( new MockIntEvent( 2 ) ); | ||
dispatcher.Dispatch( new MockIntEvent( 3 ) ); | ||
|
||
Assert.That( observer.total, Is.EqualTo( 6 ) ); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using KSPCompiler.Domain.Events; | ||
|
||
namespace KSPCompiler.Domain.Tests.Events; | ||
|
||
public class MockIntEvent( int value ) : IEvent | ||
{ | ||
public int Value { get; } = value; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
|
||
namespace KSPCompiler.Domain.Tests.Events; | ||
|
||
public class MockIntEventObserver : IObserver<MockIntEvent> | ||
{ | ||
public int total = 0; | ||
public void OnNext( MockIntEvent value ) | ||
{ | ||
total += value.Value; | ||
} | ||
public void OnCompleted() {} | ||
public void OnError( Exception error ) {} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace KSPCompiler.Domain.Events; | ||
|
||
/// <summary> | ||
/// Default implementation of <see cref="IEventDispatcher"/>. | ||
/// </summary> | ||
public sealed class EventDispatcher : IEventDispatcher | ||
{ | ||
// object : DefaultEventDispatcher.EventObservable<TEvent> | ||
// 任意のIObservable<T>型を出し入れしたいため | ||
private readonly IDictionary<Type, object> registered = new Dictionary<Type, object>(); | ||
|
||
/// <inheritdoc /> | ||
public IDisposable Subscribe<TEvent>( IObserver<TEvent> observer ) where TEvent : IEvent | ||
{ | ||
return AsObservable<TEvent>().Subscribe( observer ); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public void Dispatch<TEvent>( TEvent evt ) where TEvent : IEvent | ||
{ | ||
if( TryGetObservable( out IObservable<TEvent> observable ) ) | ||
{ | ||
( (EventObservable<TEvent>)observable ).Publish( evt ); | ||
} | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IObservable<TEvent> AsObservable<TEvent>() where TEvent : IEvent | ||
{ | ||
if( TryGetObservable( out IObservable<TEvent> result ) ) | ||
{ | ||
return result; | ||
} | ||
|
||
result = new EventObservable<TEvent>(); | ||
registered.Add( typeof( TEvent ), result ); | ||
|
||
return result; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
registered.Clear(); | ||
} | ||
|
||
private bool TryGetObservable<TEvent>( out IObservable<TEvent> result ) where TEvent : IEvent | ||
{ | ||
var type = typeof( TEvent ); | ||
|
||
result = NullObservable<TEvent>.Instance; | ||
|
||
if( !registered.TryGetValue( type, out var value ) ) | ||
{ | ||
return false; | ||
} | ||
|
||
result = (IObservable<TEvent>)value; | ||
|
||
return true; | ||
} | ||
|
||
#region IObservable Implementation | ||
|
||
#region Null Observable | ||
|
||
private class NullObservable<TEvent> : IObservable<TEvent> where TEvent : IEvent | ||
{ | ||
public static readonly IObservable<TEvent> Instance = new NullObservable<TEvent>(); | ||
|
||
private NullObservable() {} | ||
|
||
/// <inheritdoc /> | ||
public IDisposable Subscribe( IObserver<TEvent> observer ) | ||
{ | ||
return new NullDisposer(); | ||
} | ||
|
||
private class NullDisposer : IDisposable | ||
{ | ||
public void Dispose() {} | ||
} | ||
} | ||
|
||
#endregion ~Null Observable | ||
|
||
private class EventObservable<TEvent> : IObservable<TEvent> where TEvent : IEvent | ||
{ | ||
private readonly List<IObserver<TEvent>> subscribers = []; | ||
|
||
#region IObservable | ||
|
||
/// <inheritdoc /> | ||
public IDisposable Subscribe( IObserver<TEvent> observer ) | ||
{ | ||
subscribers.Add( observer ); | ||
|
||
return new AnonymousDisposer( () => | ||
{ | ||
observer.OnCompleted(); | ||
subscribers.Remove( observer ); | ||
} | ||
); | ||
} | ||
|
||
#endregion ~IObservable | ||
|
||
public void Publish( TEvent evt ) | ||
{ | ||
foreach( var subscriber in subscribers ) | ||
{ | ||
try | ||
{ | ||
subscriber.OnNext( evt ); | ||
} | ||
catch( Exception e ) | ||
{ | ||
subscriber.OnError( e ); | ||
} | ||
} | ||
} | ||
} | ||
#endregion ~IObservable Implementation | ||
|
||
#region Disposer | ||
|
||
private class AnonymousDisposer : IDisposable | ||
{ | ||
private readonly Action dispose; | ||
|
||
public AnonymousDisposer( Action dispose ) | ||
=> this.dispose = dispose; | ||
|
||
public void Dispose() | ||
=> dispose(); | ||
} | ||
|
||
#endregion ~Disposer | ||
} |
34 changes: 34 additions & 0 deletions
34
Compiler/Domain/Events/Extensions/IEventDispatcherExtension.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using System; | ||
|
||
namespace KSPCompiler.Domain.Events.Extensions; | ||
|
||
public static class IEventDispatcherExtension | ||
{ | ||
public static IDisposable Subscribe<TEvent>( this IEventDispatcher self, Action<TEvent> action ) where TEvent : IEvent | ||
{ | ||
return self.Subscribe( new AnonymousObserver<TEvent>( action ) ); | ||
} | ||
|
||
private class AnonymousObserver<T> : IObserver<T> where T : IEvent | ||
{ | ||
private readonly Action<T> onNext; | ||
private readonly Action? onCompleted; | ||
private readonly Action<Exception>? onError; | ||
|
||
public AnonymousObserver( Action<T> onNext, Action? onCompleted = null, Action<Exception>? onError = null ) | ||
{ | ||
this.onNext = onNext; | ||
this.onCompleted = onCompleted; | ||
this.onError = onError; | ||
} | ||
|
||
public void OnNext( T value ) | ||
=> onNext.Invoke( value ); | ||
|
||
public void OnCompleted() | ||
=> onCompleted?.Invoke(); | ||
|
||
public void OnError( Exception error ) | ||
=> onError?.Invoke( error ); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace KSPCompiler.Domain.Events; | ||
|
||
/// <summary> | ||
/// The base interface for configuring events. | ||
/// </summary> | ||
public interface IEvent {} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System; | ||
|
||
namespace KSPCompiler.Domain.Events; | ||
|
||
/// <summary> | ||
/// The basic interface for managing event subscribers and building event transmission management and execution. | ||
/// </summary> | ||
public interface IEventDispatcher : IDisposable | ||
{ | ||
IDisposable Subscribe<TEvent>( IObserver<TEvent> observer ) where TEvent : IEvent; | ||
void Dispatch<TEvent>( TEvent evt ) where TEvent : IEvent; | ||
IObservable<TEvent> AsObservable<TEvent>() where TEvent : IEvent; | ||
} |