High-level policy should not depend on low-level detail, low-level detail should depend on high-level policy
Depend upon abstractions, not concretions.
+ f()
Compiling Code
+ f()
Running Code
+ f()
https://xkcd.com/303/
+ f()
+ f()
+ f()
+ show()
public class LivingRoom
{
public LivingRoom()
{
}
public void EnterRoom()
{
//Painting is always there.
}
}
public class LivingRoom
{
public LivingRoom()
{
}
public void EnterRoom()
{
//Video is always there.
}
}
public class LivingRoom
{
public bool ShowVideo {get; set;}
public LivingRoom()
{
}
public void EnterRoom()
{
if(ShowVideo)
{
//Show Video.
}
{
//Show Painting.
}
}
}
+ show()
public class LivingRoom
{
private IScreen screen;
public LivingRoom(IScreen screen)
{
this.screen = screen;
}
public void EnterRoom()
{
this.screen.Show();
}
}
public class LivingRoom
{
private ILcd lcd;
public LivingRoom(ILcd lcd)
{
this.lcd = lcd;
}
public void EnterRoom()
{
lcd.Show();
}
}
Is Dependency Injection different to Dependency Inversion?
Dependency Injection is used when applying the Dependency Inversion Principle.
+ show()
+ show()
public interface IScreen
{
void Show();
}
public class Painting : IScreen
{
public void Show()
{
//Display Painting
}
}
public interface IScreen
{
void Show();
}
public class Video : IScreen
{
public void Show()
{
//Display Video
}
}
public class House
{
public LivingRoom livingRoom;
public void LivingRoom()
{
var video = new Video();
livingRoom = new LivingRoom(video);
livingRoom.EnterRoom();
}
}
public class House
{
public LivingRoom livingRoom;
public void LivingRoom()
{
var painting = new Painting();
livingRoom = new LivingRoom(painting);
livingRoom.EnterRoom();
}
}
Loosely coupled code
Increased Testability
Independent Developability
Allows for Pluggable architecture
Independent Deployability
You may never see the benefits
Can Increase Complexity
Where should we compose object graphs?
As close as possible to the application's entry point.
A Composition Root is a (preferably) unique location in an application where modules are composed together.
public class House
{
public LivingRoom livingRoom;
public void LivingRoom()
{
var painting = new Painting();
livingRoom = new LivingRoom(painting);
livingRoom.EnterRoom();
}
}
public interface IScreen
{
void Show();
}
public class Painting: IScreen
{
public void Show()
{
//Display Painting
}
}
public class Program
{
public static void Main(string[] args)
{
var painting = new Painting();
var livingRoom = new LivingRoom(painting);
var house = new House(livingRoom);
house.LivingRoom();
}
}
public class House
{
public LivingRoom livingRoom;
public House(LivingRoom livingRoom)
{
this.livingRoom = livingRoom;
}
public void LivingRoom()
{
livingRoom.EnterRoom();
}
}
public class Program
{
public static void Main(string[] args)
{
var painting = new Painting();
var livingRoom = new LivingRoom(painting);
var house = new House(livingRoom);
house.LivingRoom();
}
}
public class House
{
public LivingRoom livingRoom;
public House(LivingRoom livingRoom)
{
this.livingRoom = livingRoom;
}
public void LivingRoom()
{
livingRoom.EnterRoom();
}
}
https://simpleinjector.org/blog/2016/07/working-around-the-asp-net-core-di-abstraction/
public static class Bootstrapper
{
public static Container Container;
static Bootstrapper()
{
Container = new Container();
// Application registrations go here
Container.Verify(VerificationOption.VerifyAndDiagnose);
}
}
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<initialize>
<processor
type="DependencyInversion.CompositionRoot.InitialiseControllerFactory, DependencyInversion"
patch:instead="*[@type='Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc']"/>
</initialize>
</pipelines>
</sitecore>
</configuration>
public class InitialiseControllerFactory
{
public virtual void Process(PipelineArgs args)
{
var controllerBuilder = System.Web.Mvc.ControllerBuilder.Current;
var controllerFactory = new DependencyInversionControllerFactory(controllerBuilder.GetControllerFactory());
controllerBuilder.SetControllerFactory(controllerFactory);
}
}
public sealed class ControllerContainerAdapter: SitecoreControllerFactory
{
private static readonly Container Container = Bootstrapper.Bootstrap();
public ControllerContainerAdapter(IControllerFactory innerFactory) : base(innerFactory){}
public override IController CreateController(RequestContext requestContext, string controllerName)
{
try
{
if (controllerName.EqualsText(SitecoreControllerName))
return CreateSitecoreController(requestContext, controllerName);
var controllerType = GetControllerType(requestContext, controllerName);
try
{
return Container.GetInstance(controllerType) as IController;
}
catch (ActivationException e)
{
Log.Info($"SimpleInjector could not resolve type, {controllerType.Name}", e);
}
return ResolveController(controllerType);
}
catch (Exception e)
{
if (MvcSettings.DetailedErrorOnMissingController)
throw CreateControllerCreationException(controllerName, e);
throw;
}
}
}
public class CommandTaskContainerAdapter
{
public void Execute(Item[] items, CommandItem commandItem, ScheduleItem schedule)
{
var commandTypeValue = commandItem["Command Type"];
var commandType = Type.GetType(commandTypeValue);
var command = (ICommand) Bootstrapper.Container.GetInstance(commandType);
command.Execute(items, commandItem, schedule);
}
}
public class ExampleCommand
{
private readonly ILogAdapter _logAdapter;
public ExampleCommand(ILogAdapter logAdapter)
{
_logAdapter = logAdapter;
}
public void Execute(Item[] items, CommandItem commandItem, ScheduleItem schedule)
{
_logAdapter.LogInfo($"Hello from ExampleCommand, called from {commandItem.Name}");
}
}
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<factories>
<factory
id="FactoryContainerAdapter"
type="DependencyInversion.ContainerAdapters.FactoryContainerAdapter, DependencyInversion"/>
</factories>
</sitecore>
</configuration>
public class FactoryContainerAdapter: IFactory
{
public object GetObject(string identifier)
{
var type = Type.GetType(identifier);
return Bootstrapper.Container.GetInstance(type);
}
}
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<preprocessRequest>
<processor
ref="DependencyInversion.Pipelines.ExamplePipeline, DependencyInversion"
factory="FactoryContainerAdapter"/>
</preprocessRequest>
</pipelines>
</sitecore>
</configuration>
public class ExamplePipeline : HttpRequestProcessor
{
private readonly ILogAdapter _logAdapter;
public ExamplePipeline(ILogAdapter logAdapter)
{
_logAdapter = logAdapter;
}
public override void Process(HttpRequestArgs args)
{
_logAdapter.LogInfo($"Hello from ExamplePipeline");
}
}
Presentations on other four Principle
Come have a chat with me.
I love this shit.
Sitecore Community Slack
@gravypower
Have a read of my blog.
https://blog.gravypower.net
Much ranting, you have been warned
Mark Seemann's blog.
http://blog.ploeh.dk
https://simpleinjector.org/
The Clean Coder book
Sample Solution