skipnothing/Design Patterns

Dependency Injection

An OrderConfirmation class builds its own new SmtpMailer() in the constructor, so every test needs a live mail server and switching providers means editing the class itself. The trap is building what you could be handed.

  • ▸A class that builds new SmtpMailer() inside itself can't be tested without a real mail server.
  • ▸Moving one new from a field to a constructor parameter is the move; testing and swapping fall out of it.
  • ▸A so-called "DI framework" only automates wiring you could type by hand in main(); the parameter is the real technique.
BUILDS ON
01

Built In Means Locked In

Here is an OrderConfirmation class with one line in it: private readonly SmtpMailer mailer = new SmtpMailer();. It reads as harmless. Then you try to write a test. The test calls confirm(order), which calls mailer.send(...), which opens a socket to a real mail server. There is no mail server in your test run, so the test either hangs or fails, and even if it passed it would be firing real email at every run.

Look at why you are stuck. The SmtpMailer is built inside the field initializer, so nothing outside the class can reach it. The constructor signature is empty: OrderConfirmation() takes no arguments, so a caller reading the signature has no idea this class talks to a mail server at all, and no place to hand it a different one. The concrete SmtpMailer is welded into the class definition. To use anything else, a fake for the test, a different provider in production, you have to open OrderConfirmation and edit the line. Every class that builds its own collaborators this way carries the same weld.

THE WELD YOU CAN'T REACH

Keep going, sign up to unlock the rest

4 more parts in this topic, plus 18+ more topics in Design Patterns.

Sign up, it's freeSee the full Design Patterns
Patterns in Practice0/4#15 Singleton
#17 Template Method