Building SOLID Software with Dependency Injection Jeremy Rosenberg
Me
Who cares about software design?
What are our goals? Deliver yesterday Communicate telepathically No bugs
What are our goals? Deliver yesterday Communicate telepathically No bugs
What are our goals? Deliver quickly Communicate quickly Few bugs Fix bugs quickly Deliver yesterday Communicate telepathically No bugs
What are our goals? Deliver quickly Communicate quickly Few bugs Fix bugs quickly Deliver yesterday Communicate telepathically No bugs aka Efficiently deal with Change
What is a bad design?
A bad design causes these Deliver slowly or unpredictably Slow ramp-up for new team members Slow sharing between veteran team members
A bad design causes these Frequent bugs Simple bugs need complex changes Lots of regressions Lots of regression testing
What is a good design?
What is SOLID?
What it is Catchy acronym Principles for dealing with change
What it is Catchy acronym Principles for dealing with change
What it is Catchy acronym Principles for dealing with change SDOLI
What it’s not Framework Library Pattern Goal
What is Dependency Injection? Pattern Jives well with SOLID
What kinds of code? Object-oriented Statically typed* (code examples are in C#)
What does this method do?
– Coordinates a book checkout process for a member Checkout Storage
What does this method do? – Coordinates a book checkout process for a member Checkout Storage Is that all?
What does this class do?
Configuration DB Access
So what does this method really do? Checkout Storage
So what does this method really do? Checkout Storage – Configuration – DB access Implicit!
Let’s write a test case
SDOLI: Single Responsibility Principle “Do one thing and do it well” Applies to a method, class, service, system, …
How do we isolate the coordination?
SDOLI: Dependency Inversion Principle Depend on contracts, not implementations Goal: Decouple the single responsibility from implementations of its dependencies
SDOLI: Dependency Inversion Principle Depend on a contract at design-time – e.g. IBookRepository or BookRepositoryBase Receive an implementation at runtime – e.g. SqlBookRepository
Dependency Injection A class declares its dependencies through its own contract Providing dependencies is someone else’s job
Dependency Injection Constructor injection Property injection Method injection
Constructor injection
Our test case No configuration No database Only coordination
All this “just” for test cases? What else have we gained? – “Tightly bound, loosely coupled” Focused responsibility => readability Dependent on behavior, not implementation – Dependencies are explicit Limits unexpected side effects Straightforward (re)usage – Composability
You’re a hack!
Composition Root Constructs the object graph Lives near the entry point
Example: Console app
What about that repository?
Make the dependencies explicit
Updated Composition Root
With me?
SDOLI: Open/Closed Principle Open for extensibility, closed for modification Add behavior without changing existing code
Decorator Inject behavior into an application Man-in-the-middle
Example: Document storage
Composition Root
Add Logging
Add Authorization
Add Caching
Noticing a pattern?
Open…
Closed.
Composite Implements a contract Delegates to others with the same contract
Example
Build it like this
Use it like this
A more concrete example
Move the responsibility
Notify billing
Notify inventory
Notify shipping
Dispatch all notifications
Open / closed
SDOLI: Liskov Substitution Principle Functions that use pointers to base types must be able to use objects of derived types without knowing it. “Leaky abstraction principle” -Me
Is a Square a Rectangle?
SetWidth / SetHeight ?
Example: calculator
Usage
Guess what’s next
Oops
Plug the leak – Part 1
- Nine fingers left!
Plug the leak – Part 2
What to do Some ideas – Kill the abstraction – Adapt each calculator to something that supports input validation
Another example: more documents…
Read-only integration
Oops
Plug the leak
What to do? Some ideas – IReadOnlyDocRepo and IReadWriteDocRepo – IReadDocs and IWriteDocs – SupportsXYZ methods
How can DI help apply the LSP?
It can’t.
SDOLI: Interface Segregation Principle Thin interfaces are better than fat ones Client-focused
Example: more books…
Who’s using it?
Cache books?
Don’t need these
These are ok
What about these?
Client-specific interface
Adapter This: or this:
Slimmer caching
Focused dependencies
Let’s review
SOLID Principles S ingle Responsibility O pen/Closed L iskov Substitution I nterface Segregation D ependency Inversion
I don’t know about you…
DI Container Auto-wiring of dependencies Lifetime management
Ninject
Register
Ninject Register Resolve
Ninject Register Resolve Release
Other containers Castle Windsor Autofac StructureMap Unity …
What not to do
Don’t use Service Locator
Hides dependencies Consumers must use the same container Testing becomes un-fun
Don’t use Service Locator Composition root should be the only place referencing the DI Container. General rule: if it’s not just initializing the object graph, it’s not the composition root.
Don’t just new it
Useless interface definition Same tight coupling No way to swap, decorate, or otherwise compose implementations
Don’t be double minded
Two paths… Dependencies are still not always apparent Auto-wiring can be difficult
Building SOLID software
SOLID encourages good designs Dependency injection is a tool to bring closer alignment with SOLID
Join us
Resources The Principles of OOD Dependency Injection in.NET by Mark Seemann ploeh blog – DependencyInjection SOLID (object-oriented design)