Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This is actually simple to do once you understand that DI is about <strong>patterns and principles</strong>, not technology.</p> <p>To design the API in a DI Container-agnostic way, follow these general principles:</p> <p><strong>Program to an interface, not an implementation</strong></p> <p>This principle is actually a quote (from memory though) from <a href="http://rads.stackoverflow.com/amzn/click/0201633612" rel="noreferrer">Design Patterns</a>, but it should always be your <strong>real goal</strong>. <em>DI is just a means to achieve that end</em>.</p> <p><strong>Apply the Hollywood Principle</strong></p> <p>The Hollywood Principle in DI terms says: <em>Don't call the DI Container, it'll call you</em>.</p> <p>Never directly ask for a dependency by calling a container from within your code. Ask for it implicitly by using <strong>Constructor Injection</strong>.</p> <p><strong>Use Constructor Injection</strong></p> <p>When you need a dependency, ask for it <em>statically</em> through the constructor:</p> <pre><code>public class Service : IService { private readonly ISomeDependency dep; public Service(ISomeDependency dep) { if (dep == null) { throw new ArgumentNullException("dep"); } this.dep = dep; } public ISomeDependency Dependency { get { return this.dep; } } } </code></pre> <p>Notice how the Service class guarantees its invariants. Once an instance is created, the dependency is guaranteed to be available because of the combination of the Guard Clause and the <code>readonly</code> keyword.</p> <p><strong>Use Abstract Factory if you need a short-lived object</strong></p> <p>Dependencies injected with Constructor Injection tend to be long-lived, but sometimes you need a short-lived object, or to construct the dependency based on a value known only at run-time.</p> <p>See <a href="https://stackoverflow.com/questions/1943576/is-there-a-pattern-for-initializing-objects-created-wth-a-di-container/1945023#1945023">this</a> for more information.</p> <p><strong>Compose only at the Last Responsible Moment</strong></p> <p>Keep objects decoupled until the very end. Normally, you can wait and wire everything up in the application's entry point. This is called the <strong>Composition Root</strong>.</p> <p>More details here:</p> <ul> <li><a href="https://stackoverflow.com/questions/1475575/where-should-i-do-dependency-injection-with-ninject-2/1475861#1475861">Where should I do Injection with Ninject 2+ (and how do I arrange my Modules?)</a></li> <li><a href="https://stackoverflow.com/questions/1410719/design-where-should-objects-be-registered-when-using-windsor/1410738#1410738">Design - Where should objects be registered when using Windsor</a></li> </ul> <p><strong>Simplify using a Facade</strong></p> <p>If you feel that the resulting API becomes too complex for novice users, you can always provide a few <a href="http://en.wikipedia.org/wiki/Facade_pattern" rel="noreferrer">Facade</a> classes that encapsulate common dependency combinations.</p> <p>To provide a flexible Facade with a high degree of discoverability, you could consider providing Fluent Builders. Something like this:</p> <pre><code>public class MyFacade { private IMyDependency dep; public MyFacade() { this.dep = new DefaultDependency(); } public MyFacade WithDependency(IMyDependency dependency) { this.dep = dependency; return this; } public Foo CreateFoo() { return new Foo(this.dep); } } </code></pre> <p>This would allow a user to create a default Foo by writing</p> <pre><code>var foo = new MyFacade().CreateFoo(); </code></pre> <p>It would, however, be very discoverable that it's possible to supply a custom dependency, and you could write</p> <pre><code>var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo(); </code></pre> <p>If you imagine that the MyFacade class encapsulates a lot of different dependencies, I hope it's clear how it would provide proper defaults while still making extensibility discoverable.</p> <hr> <p>FWIW, long after writing this answer, I expanded upon the concepts herein and wrote a longer blog post about <a href="http://blog.ploeh.dk/2014/05/19/di-friendly-library" rel="noreferrer">DI-Friendly Libraries</a>, and a companion post about <a href="http://blog.ploeh.dk/2014/05/19/di-friendly-framework" rel="noreferrer">DI-Friendly Frameworks</a>.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload