Twitter icon.
Random notes on software, programming and languages.
By Adrian Kuhn

Dependency Injection vs. Virtual Classes


Every time you create an instance of a class by using the new keyword, you tightly couple your code to that class, and you will not be able to substitute that particularly implementation with a different one (at least not without recompiling the code).

Taken from an answer on stackoverflow that motivates dependency injection. Dependency injection however is not the real solution, it is rather a workaround for missing virtual classes. In this post I will show you why.

For example in

public class MyClass {
  public string sendMessage(String contents) {
    return new MailService().sendMessage(contents);
  }
}

you cannot use a different MessageService. This might not seem limiting at first sight, but think for example of testing. Your tests might be better off using a stub service that does not send actual emails.

This can and is being solved with dependency injection, yes. But the actual problem is rather a language problem: Classes are not virtual. While methods are virtual and thus looked up at runtime, classes are not.

In the following example, we can change the mail service by subclassing and overriding a method. This works because methods are virtual and thus late bound.

public class MyClass {
  public string sendMessage(String contents) {
    return newMailService().sendMessage(contents);
  }
  public MailService newMailService() {
    return new MailService();
  }
}

What a difference a single whitespace can make!

When newMailService is called, that method name is looked up in the context of the calling method. The context of the calling method is its class. Therefore, by subclassing the containing class we can provide a different mail service.

Imagine the same would happen with class names. Whenever we call new MailService() then the class name would be looked up in the containing context. Imagine further the lookup would not stop with the class but bubble up to the containing package. Then, by “subclassing” that package we might provide a different mail service.

Subclassing a package is nonsense terminology, let’s rather say that we instantiate a package … and let’s further assume that packages and classes are the same, and that our system is built of nested classes.

public class MyApplication {
  public class MyClass {
    public string sendMessage(String contents) {
      return new "MailService"().sendMessage(contents);
    }
  }
}

I have put the class name of MailService in quotes to remind you that it is the name of a virtual class name and thus looked up in the containing context.

Imagine further that we could pass the binding of classes to a class when creating it, then we could instantiate two versions of our application: one for real, and one for testing.

Application forTesting = new Application(MailService = TestMailer);
Application forReal = new Application(MailService = InterwebMailer);

Testing and production are not the only possible contexts. Imagine for example an XML parser. Typically it creates DOM nodes, but maybe you would prefer you own implementation of nodes. Maybe even the DAO objects of your application? There we go

XMLParser custom = new XMLParser(Node = Application.DAO);
XMLDocument doc = custom.new XMLDocument("example.xml");

NB, the second line is actually valid Java syntax: this is how you create an instance of an inner class when you are not in the same file as its outer class.

— So this is how dependency injection should really work!

And it is even not that far from reality. Both Beta and Newspeak (the new language by Gilad Bracha) do have virtual classes and both do have nested classes, and in both classes what I just described is how you decouple your packages in these languages.

And, hup hup hup, another design pattern has been disguised as a missing language feature :)

7 Responses to “Dependency Injection vs. Virtual Classes”

  1. Brian Harris Says:

    Good insight! Do you know if Beta or Newspeak also have virtual “static methods”?

  2. Randal L. Schwartz Says:

    In Perl, I just teach “no hardwired classnames”. So instead of:

    sub foo {
     ...
     return FriendClass->new->something(@xyz);
    }
    

    we write

    sub foo {
      ...
      return $self->friendclass->new->something(@xyz);
    }
    
    sub friendclass { 'FriendClass' }

    And yeah, this does the trick nicely.

  3. akuhn Says:

    @brian, I dont know Beta to that detail. In Newspeak at least there is no such thing as static at all (except maybe for the outermost context of the outermost nested class, recall each class is nested in another class). Gilad Bracha, the inventor of Newspeak, is an outspoken opponent of anything static. Read eg Cutting out Static on his blog.

  4. Gilad Bracha Says:

    Hi Adrian,

    Nice post; one more point I’d like to make:

    When you use a special syntax like new Foo(x) to instantiate classes, you’re restricting yourself. Say you want to look up instances in a cache, rather than allocating fresh ones. You would like to replace the class Foo with an object that looks up instances in the cache (say, based on the value of x, the argument to the constructor. That is why it is important to do away with traditional constructors even when classes are virtual. Therefore, in Newspeak, constructors (aka factories) have names and their use is indistinguishable from ordinary methods.

  5. akuhn Says:

    Hi Gilad and welcome to my small blog!

    I am glad that you mention named constructors! I learned to love them in Smalltalk and Newspeak, and only left them out in this post to avoid confusing the uninitiated reader with two new concepts at once. Java’s new keyword is terribly limited, and “injection is the new new” can’t be the solution either. Named constructors are awesome! Also, passing a block to them (that receives an inner builder class as argument) even offers a nice way to implement a small builder DSL …hmm, mebbe that should go into a post if its own :)

  6. SCG : Should DI and GC be unified? Says:

    [...] post is somehow an answer to this other post, which discusses dependency injection vs. virtual classes. Neither is optimal in my view, though I [...]

  7. Niko Says:

    @Gilad, I couldn’t agree more. I tried to argue to the same point here.

For.example is Digg proof thanks to caching by WP Super Cache!