Is There an Alternative to Bastard Injection? (Aka Poor Man's Injection via Default Constructor)

Is there an alternative to bastard injection? (AKA poor man's injection via default constructor)

As far as I understand, this question relates to how to expose a loosely coupled API with some appropriate defaults. In this case, you may have a good Local Default, in which case the dependency can be regarded as optional. One way to deal with optional dependencies is to use Property Injection instead of Constructor Injection - in fact, this is sort of the poster scenario for Property Injection.

However, the real danger of Bastard Injection is when the default is a Foreign Default, because that would mean that the default constructor drags along an undesirable coupling to the assembly implementing the default. As I understand this question, however, the intended default would originate in the same assembly, in which case I don't see any particular danger.

In any case you might also consider a Facade as described in one of my earlier answers: Dependency Inject (DI) "friendly" library

BTW, the terminology used here is based on the pattern language from my book.

What is the real difference between Bastard Injection and Poor Man's Injection

When it comes to DI, there's a lot of conflicting use of terminology out there. The term Poor Man's DI is no exception. To some people, it means one thing and to others it means something different.

One of the things I wanted to do with the book was to supply a consistent pattern language for DI. When it came to all of those terms with conflicting use, I had two options: Come up with a completely new term, or pick the most prevalent use (according to my subjective judgment).

In general, I've preferred to re-use existing terminology instead of making up a completely new (and thus alien) pattern language. That means that in certain cases (such as Poor Man's DI), you may have a different notion of what the name is than the definition given in the book. That often happens with patterns books.

At least I find it reassuring that the book seems to have done its job of explaining exactly both Poor Man's DI and Bastard Injection, because the interpretation given in the O.P. is spot on.

Regarding the real benefit of a DI Container I will refer you to this answer: Arguments against Inversion of Control containers


P.S. 2018-04-13: I'd like to point out that I've years ago come to acknowledge that the term Poor Man's DI does a poor (sic!) job of communicating the essence of the principle, so for years, now, I've instead called it Pure DI.


P.P.S. 2020-07-17: We removed the term Bastard Injection from the second edition. In the second edition we simply use the more general term Control Freak to specify that your code "depend[s] on a Volatile Dependency in any place other than a Composition Root."

Mark Seemann's conflicting statements about Bastard Injection. Need some clarifications

Since there are many questions here, I'll first attempt to provide an synthesis on my view on the subject, and then answer each question explicitly based on this material.

Synthesis

When I wrote the book, I first and foremost attempted to describe the patterns and anti-patterns I'd witnessed in the wild. Thus, the patterns and anti-patterns in the book are first and foremost descriptive, and only to a lesser degree prescriptive. Obviously, dividing them into patterns and anti-patterns imply a certain degree of judgement :)

There are problems with Bastard Injection on multiple levels:

  • Package dependency
  • Encapsulation
  • Ease of use

The most dangerous problem is related to package dependencies. This is the concept I've attempted to make more actionable by the introduction of the terms Foreign Default versus Local Default. The problem with Foreign Defaults is that they drag along hard-coupled dependencies, which makes (de/re)composition impossible. A good resource that deals more explicitly with package management is Agile Principles, Patterns, and Practices.

On the level of encapsulation, code like this is difficult to reason about:

private readonly ILog log;
public MyConsumer(ILog log)
{
this.log = log ??LogManager.GetLogger("My");
}

Although it protects the class' invariants, the problem is that in this case, null is an acceptable input value. This is not always the case. In the example above, LogManager.GetLogger("My") may only introduce a Local Default. From this code snippet, we have no way to know if this is true, but for the sake of argument, let's assume this for now. If the default ILog is indeed a Local Default, a client of MyConsumer can pass in null instead of ILog. Keep in mind that encapsulation is about making it easy for a client to use an object without understanding all the implementation details. This means that this is all a client sees:

public MyConsumer(ILog log)

In C# (and similar languages) it's possible to pass null instead of ILog, and it's going to compile:

var mc = new MyConsumer(null);

With the above implementation, not only will this compile, but it also works at run-time. According to Postel's law, that's a good thing, right?

Unfortunately, it isn't.

Consider another class with a required dependency; let's call it a Repository, simply because this is such a well-known (albeit overused) pattern:

private readonly IRepository repository;
public MyOtherConsumer(IRepository repository)
{
if (repository == null)
throw new ArgumentNullException("repository");

this.repository = repository;
}

In keeping with encapsulation, a client only sees this:

public MyOtherConsumer(IRepository repository)

Based on previous experience, a programmer may be inclined to write code like this:

var moc = new MyOtherConsumer(null);

This still compiles, but fails at runtime!

How do you distinguish between these two constructors?

public MyConsumer(ILog log)
public MyOtherConsumer(IRepository repository)

You can't, but currently, you have inconsistent behaviour: in one case, null is a valid argument, but in another case, null will cause a runtime exception. This will decrease the trust that every client programmer will have in the API. Being consistent is a better way forward.

In order to make a class like MyConsumer easier to use, you must stay consistent. This is the reason why accepting null is a bad idea. A better approach is to use constructor chaining:

private readonly ILog log;

public MyConsumer() : this(LogManager.GetLogger("My")) {}

public MyConsumer(ILog log)
{
if (log == null)
throw new ArgumentNullException("log");

this.log = log;
}

The client now sees this:

public MyConsumer()
public MyConsumer(ILog log)

This is consistent with MyOtherConsumer because if you attempt to pass null instead of ILog, you will get a runtime error.

While this is technically still Bastard Injection, I can live with this design for Local Defaults; in fact, I sometimes design APIs like this because it's a well-known idiom in many languages.

For many purposes, this is good enough, but still violates an important design principle:

Explicit is better than implicit

While constructor chaining enables a client to use MyConsumer with a default ILog, there's no easy way to figure out what the default instance of ILog would be. Sometimes, that's important too.

Additionally, the presence of a default constructor exposes a risk that a piece of code is going to invoke that default constructor outside of the Composition Root. If that happens, you've prematurely coupled to objects to each other, and once you've done that, you can't decouple them from within the Composition Root.

Thus, there's less risk involved in using plain Constructor Injection:

private readonly ILog log;

public MyConsumer(ILog log)
{
if (log == null)
throw new ArgumentNullException("log");

this.log = log;
}

You can still compose MyConsumer with the default logger:

var mc = new MyConsumer(LogManager.GetLogger("My"));

If you want to make the Local Default more discoverable, you can expose it as a Factory somewhere, e.g. on the MyConsumer class itself:

public static ILog CreateDefaultLog()
{
return LogManager.GetLogger("My");
}

All this sets the stage for answering the specific sub-questions in this question.

1. Does Bastard Injection anti-pattern also occur when default implementation of dependency is a Local Default?

Yes, technically, it does, but the consequences are less severe. Bastard Injection is first and foremost a description that will enable you to easily identify it when you encounter it.

Please note that the above illustration from the book describes how to refactor away from Bastard Injection; not how to identify it.

2. [Should] optional dependencies with local defaults [...] also be avoided?

From a package dependency perspective, you don't need to avoid those; they are relatively benign.

From a usage perspective, I still tend to avoid them, but it depends on what I'm building.

  • If I create a reusable library (e.g. an OSS project) that many people will use, I may still choose Constructor Chaining in order to make it easier to get started with the API.
  • If I'm creating a class only for use within a particular code base, I tend to entirely avoid optional dependencies, and instead compose everything explicity in the Composition Root.

3. Is he also implying that Foreign Default makes parallel development more difficult, while Local Default doesn't?

No, I don't. If you have a default, the default must be in place before you can use; it doesn't matter whether it's Local or Foreign.

Dependency injection - is there an alternative to this very large constructor?

How about using a DbContext object:

$dbContext = new DbContext($logger, $filter, $database, $session);

Then your "Car" is:

$car = new Car($dbContext, $color, $amountOfWheels);

C# IoC configuration: how can I inject an object when one of its parameters it's not an already injected object?

The lambda function passed to AddScoped takes an IServiceProvider as input. You can use this to resolve an IHttpClientFactory by calling GetService<IHttpClientFactory>().

services.AddScoped<IInterface, Concrete>(sp
=> new Concrete(sp.GetService<IHttpClientFactory>(), "s1", "s2"));

Inject a backend and a frontend instance into the Game class from the Main class with Dependency Injection

In this simplified case the dependency injection means just creating the instances of the classes you need in the main() method and passing them to the constructor of the Game class:

public class Game {

privat final GameBackend backend;
privat final GameUI ui;

public Game(GameBackend gameBackend, GameUI gameUI) {
this.backend = gameBackend;
this.ui = gameUI;
}

public void run(){
...
}
}

Main class:

public class Main {
public static void main(String[] args) {

GameBackend gameBackend = new GameBackendImpl();
GameUI gameUI = new GameUIImpl();

Game game = new Game(gameBackend, gameUI);

game.run();
}
}

But this approach, of course, doesn't bring the advantages of the Dependency Injection/Inversion of Control.

I guess you are looking for the possibility to inject your dependencies automatically and to be able to configure them according to your needs (different implementations for different environments etc.). In this case you can use a framework like Spring which will take control on the program flow, instantiating and injecting the dependencies into corresponding classes.

The simple implementation using Spring Boot would be the following:

GameBackend.java:

public interface GameBackend {
String getVersion();
}

GameUI.java:

public interface GameUI {
String getVersion();
}

GameBackendImpl.java:

@Component
public class GameBackendImpl implements GameBackend {
@Override
public String getVersion() {
return "Backend_v1";
}
}

GameUiImpl.java:

@Component
public class GameUiImpl implements GameUI {
@Override
public String getVersion() {
return "UI_v1";
}
}

Game.java:

@Component
public class Game {

private final GameBackend backend;
private final GameUI ui;

@Autowired
public Game (GameBackend backend, GameUI ui) {
this.backend = backend;
this.ui = ui;
}

public void run() {
System.out.println("Game bean instantiated with the UI '"+this.ui.getVersion()+"' and Backend '"+this.backend.getVersion()+"' versions");
}
}

SpringBootDemoApp.java:

@SpringBootApplication
public class SpringBootDemoApp implements CommandLineRunner {

@Autowired
ApplicationContext applicationContext;

public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApp.class, args);

}

@Override
public void run(String... args) {
((Game)(applicationContext.getBean("game"))).run();
}
}

What happens here:

Spring scans classes in the package and finds Beans annotated with the @Component annotation.
Upon instantiating the Game class, Spring considers its constructor, which is annotated with @Autowired and requires exactly 2 parameters (dependencies), and looks for the candidates to be injected. In this case there is only one candidate per each type (you can create multiple different implementations of the GameBackend and GameUI interfaces). Thus, all the preparation work is done by the framework.
There is much more happening under the hood of course.

In the main class, we can check the result by getting the instance of the Game class (Bean with the name "game) from the ApplicationContext. Calling its run() method will print:

"Game bean instantiated with the UI 'UI_v1' and Backend 'Backend_v1' versions"

NOTE: getting and using the instance of the Game class from the main class doesn't bring any advantages and is done only for the demonstration.

There are more DI/IoC frameworks for Java (CDI in J2EE, Guice etc.)



Related Topics



Leave a reply



Submit