Everything You Love about Java is Everything I Love About Good Design

I just read Why I love everything you hate about Java. You should too. There are some very good points about modularity in there.

Unfortunately they are all mixed up with some unnecessarily combative us-vs-them rhetoric. Apparently in Nick Kallen’s view, as a Rubyist I slap my hands over my ears and start rocking back and forth when I hear the words “Dependency Injection” or “Factory”.

Let us be clear. If you are a Rubyist and your hackles do rise at the phrase “Dependency Injection”, you need to check yourself. Using a dynamic language shouldn’t mean throwing the Gang of Four out the window. And anyway, you probably use software daily – such as Rails – which is just lousy with enterprise design patterns.

The patterns Nick cites – Dependency Injection, Factory, and Decorator – are patterns I use daily. They are some of my favorites, to the point that they are practically second nature. For instance, here’s a paraphrased and simplified example from some production code I wrote:

def execute_shell_command(command_line, options={})
  shell_command_maker = options.fetch(:shell_command_maker) {
    ShellCommand.method(:new)
  }

  command = shell_command_maker.call(command_line, options)
  command.execute!
end

This example combines Factory and Dependency Injection and tops them with some Convention Over Configuration sauce. The dependency on a command line object is injectable, and we inject it by passing in an optional factory named :shell_command_maker. This factory, rather than having to be a Factory class, can be any callable object – such as a lambda. If no option is specified, the code uses ShellCommand.new() to instantiate the object. We can inject a different command line class:

execute_shell_command("ls", :shell_command_maker => RemoteCommand.method(:new))

But we can just as easily inject something fancier. Lets inject a ShellCommand wrapped in a logging Decorator:

require 'delegator'
class CommandLogger < SimpleDelegator
  def initialize(shell_command, logger=Logger.new($stderr))
    @command = command_line
    @logger = logger
    super(shell_command)
  end

  def execute!
    @logger.info "Executing '#{@command}'"
    super
  end
end

logger = Logger.new($stdout)
make_logged_shell_command = lambda do |command_line, options|
  CommandLogger.new(ShellCommand.new(command_line, options), logger)
end
options = {
  :shell_command_maker => make_logged_shell_command
}
execute_shell_command("ls", options)
execute_shell_command("ps aux", options)
# ...

What I find repugnant about Java development is not the use of industrial strength patterns, but the fact that using those patterns in Java is so awkward, high-ceremony and obtrusive that people wind up writing entire books on simple concepts such as dependency injection. A fact which Nick seems to have grasped as well, since he’s writing his examples in Scala, a language with a level of expressiveness similar to that of Ruby.

The Java ecosystem warps your brain into a mode of thinking where modularity patterns like DI and decoration are like ancient gods which can only be invoked with a great deal of pomp and ceremony. And I think that perspective tends to make it hard to see that other programmers use the same patterns, just with less fanfare. For an even better take on this topic than mine, read Jamis Buck’s classic account of writing and discarding two Ruby DI frameworks.

5 comments

  1. Minor thing, but options={} is worthless in that wrapper method. Because you call fetch, the user of this api (who doesn’t pass in options, because it has a default), can quite happily call this method without any argument errors. Instead, they will hit an error on the fetch, which is far more confusing than an argument error.

  2. There is a tendency for new languages to forego strictness and pattern enforcement. The problem is if you don’t enforce it, some half ass programmer will break it. This is why no language will take over Java as the king of enterprise for the foreseeable future. Why Java designers are so hesitant to introduce the lambda? Because though powerful the construct is liable to much abuse that can greatly undermine the explicitness and code maintainability that is central to Java’s success. And Java designers don’t make Java more expressive for a reason, to keep the learning curve low and accessible to more programmers, at the minor expense of verbosity. Ultimately though the best programming language is one that reads like English and needs no documentation, that’s the direction we should move toward, not how compact our code is.

Leave a Reply

Your email address will not be published. Required fields are marked *