Skip to content

Commit

Permalink
Added an example of using interception policies against open generic …
Browse files Browse the repository at this point in the history
…types to the docs. Closes GH-391
  • Loading branch information
jeremydmiller committed Oct 8, 2015
1 parent 4fc5d2e commit 145d955
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 45 deletions.
29 changes: 29 additions & 0 deletions documentation/generics.md
Expand Up @@ -220,3 +220,32 @@ IRepository<TDocument, TQuery> StructureMap.Testing.Acceptance Tra
===================================================================================================================================================
</pre>



## Example 3: Interception Policy against Generic Types

Several years ago I described an approach for [using an Event Aggregator in a WPF application](http://codebetter.com/jeremymiller/2009/07/24/how-i-m-using-the-event-aggregator-pattern-in-storyteller/) that relied on StructureMap interception to register any object that
StructureMap built with the active [EventAggregator](http://martinfowler.com/eaaDev/EventAggregator.html) for the system *if that object was recognized as a listener to the event aggregator*. I thought that approach worked out quite well, so let's talk about how you could implement
that same design with the improved interception model introduced by StructureMap 3.0 (the event aggregator and StructureMap interception worked out well,
but I'm very happy now that I ditched the old WPF client and replaced it with a web application using React.js instead).

First off, let's say that we're going to have this interface for our event aggregator:

<[sample:IEventAggregator]>

To register a listener for a particular type of event notification, you would implement an interface called `IListener<T>` shown below
and directly add that object to the `IEventAggregator`:

<[sample:IListener<T>]>

In the application I'm describing, all of the listener objects were presenters or screen controls that were created by StructureMap, so it was convenient to allow StructureMap to register newly created objects with the `IEventAggregator` in an activation interceptor.

What we want to do though is have an interception policy that only applies to any concrete type that implements some interface that
closes `IListener<T>`:

<[sample:EventListenerRegistration]>

To see our new interception policy in action, see [this unit test from GitHub](https://github.com/structuremap/structuremap/blob/master/src/StructureMap.Testing/Samples/Interception/Event_Aggregator_Registration.cs):

<[sample:use_the_event_listener_registration]>

3 changes: 2 additions & 1 deletion documentation/interception-and-decorators.md
@@ -1,7 +1,8 @@
<!--Title: Interception and Decorators-->
<!--Url: interception-and-decorators-->

All of the samples from this topic are part of the [user acceptance tests](https://github.com/structuremap/structuremap/blob/master/src/StructureMap.Testing/Acceptance/interception_acceptance_tests.cs) in the main codebase.
All of the samples from this topic are part of the [user acceptance tests](https://github.com/structuremap/structuremap/blob/master/src/StructureMap.Testing/Acceptance/interception_acceptance_tests.cs) in the main codebase. There is also another example of using an interception policy with
open generic types at the bottom of <[linkto:generics]>

Improving the interception facilities and the means of applying [decorators](https://en.wikipedia.org/wiki/Decorator_pattern) during object construction was one of the primary
goals of the big 3.0 release and is significantly different than the older 2.5/2.6 model.
Expand Down
8 changes: 4 additions & 4 deletions src/CommonAssemblyInfo.cs
Expand Up @@ -3,7 +3,7 @@
[assembly: AssemblyDescription("IoC Container for .Net")]
[assembly: AssemblyProduct("StructureMap")]
[assembly: AssemblyCopyright("Copyright 2004-2015 Jeremy D. Miller, Joshua Flanagan, Frank Quednau, Tim Kellogg, et al. All rights reserved.")]
[assembly: AssemblyTrademark("22971087c8985d2a081d633a7e47abc6c75cad70")]
[assembly: AssemblyVersion("4.0.0.50832")]
[assembly: AssemblyFileVersion("4.0.0.50832")]
[assembly: AssemblyInformationalVersion("4.0.0.50832")]
[assembly: AssemblyTrademark("4fc5d2ef1b914e90de20edc557ffda070297460b")]
[assembly: AssemblyVersion("4.0.0.51055")]
[assembly: AssemblyFileVersion("4.0.0.51055")]
[assembly: AssemblyInformationalVersion("4.0.0.51055")]
Expand Up @@ -13,6 +13,7 @@ namespace StructureMap.Testing.Samples.Interception
[TestFixture]
public class Event_Aggregator_Registration
{
// SAMPLE: use_the_event_listener_registration
[Test]
public void use_the_event_listener_registration()
{
Expand All @@ -31,6 +32,7 @@ public void use_the_event_listener_registration()

listener.Messages.Single().ShouldBeTheSameAs(message);
}
// ENDSAMPLE
}

public class BooMessage
Expand All @@ -47,6 +49,7 @@ public void Handle(BooMessage message)
}
}

// SAMPLE: EventListenerRegistration
public class EventListenerRegistration : IInterceptorPolicy
{
public string Description
Expand All @@ -64,16 +67,20 @@ public IEnumerable<IInterceptor> DetermineInterceptors(Type pluginType, Instance
}
}
}
// ENDSAMPLE

public interface IListener
{
}

// SAMPLE: IListener<T>
public interface IListener<T>
{
void Handle(T message);
}
// ENDSAMPLE

// SAMPLE: IEventAggregator
public interface IEventAggregator
{
// Sending messages
Expand All @@ -83,15 +90,8 @@ public interface IEventAggregator
// Explicit registration
void AddListener(object listener);
void RemoveListener(object listener);

// Filtered registration, experimental
If<T> If<T>(Func<T, bool> filter);
}

public interface If<T>
{
object PublishTo(Action<T> action);
}
// ENDSAMPLE

public class EventAggregator : IEventAggregator
{
Expand Down Expand Up @@ -130,11 +130,6 @@ public void RemoveListener(object listener)
withinLock(() => _listeners.Remove(listener));
}

public If<T> If<T>(Func<T, bool> filter)
{
return new IfExpression<T>(filter, this);
}

#endregion

private object[] all()
Expand Down Expand Up @@ -181,33 +176,6 @@ public void RemoveAllListeners(Predicate<object> filter)
_listeners.RemoveAll(filter);
}

#region Nested type: IfExpression

internal class IfExpression<T> : If<T>
{
private readonly EventAggregator _aggregator;
private readonly Func<T, bool> _filter;

public IfExpression(Func<T, bool> filter, EventAggregator aggregator)
{
_filter = filter;
_aggregator = aggregator;
}

#region If<T> Members

public object PublishTo(Action<T> action)
{
var listener = new FilteredListener<T>(_filter, action);
_aggregator.AddListener(listener);

return listener;
}

#endregion
}

#endregion
}

public class FilteredListener<T> : IListener<T>
Expand Down

0 comments on commit 145d955

Please sign in to comment.