• No results found

IoC Container

In document Practical Microsoft Visual Studio 2015 (Page 143-146)

An IoC (or Inversion of Control) container is a framework to facilitate dependency inversion. It provides the ability for code to depend at compile-time almost entirely on abstractions and leave building up instances of the concrete types to the framework. It's called an inversion of control container because you configure the container to call back into your code (or someone else's code, but outside the IoC container code) to perform the building up of the required instances. That is control flow is inverted from typical control flow from the framework back into client code.

IoC containers should be used with caution. They should be used for a specific need other than simply dependency inversion. Dependency inversion can be accomplished simply by dependency injection. IoC containers should be used when simple dependency injection cannot be performed. While it’s a bad example (see my caveat following), I could use an IoC container to build up the Car object that I described in constructor injection. Listing 6-7 shows using the NInject container to configure it to be able to instantiate a Car object with specific parts.

Listing 6-7. Using an IoC Container to Build Up the Car Object Described in the Constructor Injection

var kernel = new Ninject.StandardKernel(); kernel.Bind<IEngine>().To<V8Engine>(); kernel.Bind<IChassis>().To<StandardChassis>(); kernel.Bind<IConsole>().To<StandardConsole>(); kernel.Bind<ITransmission>().To<AutomaticTransmission>(); kernel.Bind<IExhaustSystem>().To<StandardExhaustSystem>(); kernel.Bind<IEnumerable<ITire>>().ToMethod(_ => new AllSeasonRadialTire[4]); var car = kernel.Get<Car>();

Using IoC containers are like service locators and singletons, in that they add a new dependency and accidental complexity (potentially a lot of it), as the semantics to using that specific container can be complex. You end up being very tightly coupled to one type of container due to the distinct semantics of that container.

133

Command

There are a couple types of command patterns. One has to do with messaging and the other has to do with a behavior. They are similar in that they encapsulate a copy of the data required to fulfill a request. But the behavior pattern also models executing the command. The command message would be a specific (over- the-wire) type of parameter object. See Chapter 4 for more details on command messages.

Basically, the command object encapsulates all the data required to invoke a request without having the requestor know anything about the particular data. The requestor would know about a command object (maybe a specific type, maybe a generic type) and whether to execute it. Think of a Start Car command. You don't want to know how electricity works to cause a motor to rotate to cycle an engine and spark the cylinders, you just want to start the car. The command pattern allows you to encapsulate details like that.

Typically, the command pattern is implemented as an implementation of a generic class or interface that implements one Execute method. Anything that uses a command object would simply invoke the Execute method when it needed to. The rest of the work would be performed by the command implementation.

You can see this pattern used with Windows Presentation Foundation (WPF), which expands the pattern slightly to include members like CanExecute and CanExecuteChanged .

Decorator

The decorator pattern is probably one of the most powerful patterns that facilities decoupling and composability. The decorator pattern is named because it "decorates" or adds to existing functionality. In order for the decorator pattern to work, we must first start with an abstraction. The decorator then implements that abstraction and wraps another implementation of the same abstraction and calls through to wrapped implementation. Although the decorator pattern is also a wrapper like the adapter pattern, a decorator is not an adapter because it performs no adaption to publish a different interface. (See the section called “Liskov Substitution Principle” for more information.).

The power of the decorator pattern is that if you implement a highly decoupled system and follow principles like the dependency inversion principle or the interface segregation principle, you likely have everything you need to utilize the decorator pattern.

What can be decorations? Well, pretty much anything. The key thing is that they are additions to existing data/behavior. Ensure you follow the Liskov substitution principle so that you're not changing the behavior- wrapped instance (such as changing inputs to make it behave differently). Often what decorations mean are quality attributes (or non-functional requirements): logging, authorization, auditing, etc. But don't let that limit what you can do with decorators; the canonical example is to provide scrolling ability to an existing window class.

A couple of the open source projects I work on are a set of messaging primitives and a set of messaging implementations. One of the primitives is a bus modeled as the interface IBus . The implementations include an implementation of IBus that facilitates connections of messages to message handlers. I could easily decorate an IBus implementation to provide logging. Listing 6-8 shows how the Handle method could be decorated to log the start of sending a message, the message, when the send completed, and the time it took to send the message.

Listing 6-8. Decorating the Handle Method to Log the Start of Sending a Message

public void Handle(IMessage message) {

traceListener.WriteLine(

$"Message sent at {clock.Now}. "+ "Message details: {message}"); var stopwatch = Stopwatch.StartNew();

134

wrappedBus.Handle(message);

traceListener.WriteLine($"Message processing completed "+ "at {clock.Now}. Duration: {stopwatch.Elapsed}"); }

Listing 6-9 shows a complete LoggingBus class. More details on IBus can be found at http://bit. ly/1RINTyi .

Listing 6-9. A Complete LoggingBus Class

public class LoggingBus : IBus {

private readonly IBus wrappedBus;

private readonly TraceListener traceListener; private readonly IClock clock;

public LoggingBus(IBus wrappedBus, TraceListener traceListener, IClock clock)

{

if (wrappedBus == null)

throw new ArgumentNullException(nameof(wrappedBus)); if (traceListener == null)

throw new ArgumentNullException(nameof(traceListener)); if (clock == null)

throw new ArgumentNullException(nameof(clock)); this.wrappedBus = wrappedBus;

this.traceListener = traceListener; this.clock = clock;

}

public void Handle(IMessage message) {

traceListener.WriteLine($"Message sent at {clock.Now}. "+ "Message details: {message}");

var stopwatch = Stopwatch.StartNew(); wrappedBus.Handle(message);

traceListener.WriteLine($"Message processing completed at "+ "{clock.Now}. Duration: {stopwatch.Elapsed}");

}

public void AddTranslator<TIn, TOut>(IPipe<TIn, TOut> pipe) where TIn : IMessage where TOut : IMessage

{

traceListener.WriteLine($"Translator added to translate "+ "{typeof(TIn)} to {typeof(TOut)} at {clock.Now}"); wrappedBus.AddTranslator(pipe);

135

public void AddHandler<TIn>(IConsumer<TIn> consumer) where TIn : IMessage

{

traceListener.WriteLine($"Handler added to consume "+ "{typeof(TIn)} at {clock.Now}");

wrappedBus.AddHandler(consumer); }

public void RemoveHandler<TMessage>(IConsumer<TMessage> consumer) where TMessage : IMessage

{

traceListener.WriteLine($"Handler of message "+ "{typeof(TMessage)} removed at {clock.Now}"); wrappedBus.RemoveHandler(consumer);

} }

One side-effect of the decorator pattern is that you can more closely satisfy the single responsibility principle where the wrapped instance sticks to one responsibility and each decorator another responsibly.

Façade

In many systems, there is a number of complex classes that might perform complex things (maybe legacy, maybe they're just complex). Regardless of why they're complex, you may find that certain usage scenarios use a consistent subset of several instances of classes. This is where the façade pattern comes to your rescue.

The façade pattern provides a new, simplified interface over another set of interfaces (a façade). This differs from mediator pattern in the façade does not provide new functionality, it just passes through to a contained or aggregated set of objects.

I've successfully used patterns like façade to implement transitional architectures where the FAÇADE was the ideal interface to reach in the future. Things would use the façade while the implementation behind the façade evolved to the new, desired state. Code that used the façade never needed to change while the implementation evolved.

In document Practical Microsoft Visual Studio 2015 (Page 143-146)