http://java.sun.com/javaone http://java.sun.com/javaone http://java.sun.com/javaone
JavaOne - Experiencing Java technology through education, industry, and community
  2009 Platinum Cosponsor
Intel
  Cosponsors
Cosponsors
  Gen Session Cosponsors
General Session Cosponsors
  Media Sponsors
Media Cosponsors
Home > 'Design Patterns' for Dynamic Languages on the JVM Machine

'Design Patterns' for Dynamic Languages on the JVM Machine


Neal Ford is a popular presenter at computer science conferences, and he showed why at the 2009 JavaOne conference. His session, "Design Patterns" for Dynamic Languages on the JVM (TS-4961), was largely a critique of the book Design Patterns by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (collectively known as the "Gang of Four"). His updates to the patterns described in that book showed that patterns for older languages have more elegant solutions in more powerful modern languages.

Combining sweeping overviews with many code examples, Ford illustrated design patterns, which he decribed simply as approaches to solving common problems. Through examples, he showed how dynamic languages provide the tools to solve those problems. The examples were so numerous that few of them can be presented in this article.

Fortunately, Ford has made his slides available at his web site, nealford.com. The slides won't include his entertaining presentation, though. For that, you will have to wait for the 2009 JavaOne conference transcriptions.

Ford took many opportunities to contrast languages like Java that require significant code to instantiate objects and methods, which he described as "ceremonial," and dynamic languages like Ruby or Groovy, which he described as "essential." Ceremony means you have to surround the core of what you're trying to accomplish with a cloud of definitions, type statements, and so on. Languages that emphasize essence strip away as much ceremony as possible and emphasize human-readable syntax.

Ford explored the following design patterns:

  • Iterator
  • Command
  • Strategy
  • Template
  • Interpreter
  • Decorator
  • Adaptor

He closed by describing dynamic language patterns, which are not mentioned in the gang-of-four book.

Before diving in to the design patterns, Ford explained his aversion to UML (unified modeling language) diagrams, which are seen abundantly in the book. In his opinion, these diagrams are too technical for nontechnical people, but too simple and uninformative for technical people. Nevertheless, he acknowledged that discussion of design patterns usually immerses one in UML.

Interator Design Pattern

Iterators provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

This design pattern has been subsumed into the modern wave of dynamic languages. For example, the following Groovy code acts as an iterator:

def printAll(container) {
      for (item in container) { println item }
}
def numbers = [1,2,3,4]
def months = [Mar:31, Apr:30, May:31]
def colors = [java.awt.Color.BLACK,  java.awt.Color.WHITE]
printAll numbers
printAll months
printAll colors

numbers.each { n ->
    println n
}

The essence of the iterator pattern is provided by the printAll definition.

Internal vs. External Iterators

Internal iterators, also called push iterators, return each item in the collection, one after the other. In Groovy, the .each expression is an internal iterator.

External iterators, also called pull iterators, are more familiar to Java programmers. JRuby 1.6 has no default syntax for external iterators; JRuby 1.8 has enumerators on collections that can be used to create external iterators. Groovy provides external iterators via the iterator() method.

Command Design Pattern

The idea behind the Command Design Pattern is to bundle functionality in a class, encapsulating it as an object. You can then parameterize clients with different requests, queue or log requests, and support undoable operations.

Languages with closures have the command design pattern built in -- you need only add structure as necessary.

As an example, Ford presented the following Groovy code:

def count = 0
def commands = []

1.upto(10) { i ->
      commands.add { count ++ }
}

println "count is initially ${count}"
commands.each { cmd ->
          cmd()
}
println "did all commands, count is ${count}"

The example code creates a count and an array of commands from 1 to 10. Expressions inside curly braces in Groovy are code blocks, so the expression commands.add { count ++ } adds a count iterator to each command. The commands.each expression executes each code block, and the result of execution is the following output:

count is intially 0
did all commands, count is 10

Ford then presented a more elaborate example in Groovy that implemented an undoable operation.

Strategy Design Pattern

In a Strategy design pattern, you define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.

Ford presented a Groovy example of a simple Strategy design pattern in which two different algorithms were used to calculate the product of two numbers. Each algorithm was instantiated as a class and placed into an array of classes. Then, the .each keyword was used to iterate through the array of algorithm classes.

With fewer lines of code, Ford showed that the structure required by the Strategy design pattern was unnecessary in Groovy because the algorithms can be defined with in-line code blocks and executed without reference to a class or object.

Template Design Pattern

In the Template design pattern, you define the skeleton of an algorithm in a parent class, deferring some steps to subclasses. The Template pattern lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

For example, you may know that three different things must be done for each object processed by a given class, but exactly how the three things are implemented depends on the objects being processed. The skeleton class would define the algorithms, but their implementations would be deferred to subclasses.

Ford presented another Groovy example to illustrate the pattern, first in the traditional way described by the Design Patterns book. He then showed how, by passing algorithms as code blocks, the design pattern could be simplified.

Interpreter Design Pattern

In the Interpreter design pattern, you define a representation for a given language's grammar, along with an interpreter that uses the representation to interpret sentences in the language.

Ford wryly observed that the use of this pattern seems to indicate that you've chosen the wrong language for the job. Nevertheless, Ford presented an example in Ruby that built a simple domain-specific language atop Groovy.

Decorator Design Pattern

In the Decorator design pattern, you attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. Through Groovy examples, Ford first presented a traditional decorator pattern and then improved on it by making it more dynamic, then demonstrating a "decorate in place" approach.

Finally, Ford used JRuby to illustrate what he called an "ultimate decorator" pattern, in which a recorder method is defined. Calls to the recorder are not invoked immediately, but instead are saved away for later use.

Adaptor Design Pattern

In the Adapter design pattern, you convert the interface of a class into another interface that clients expect. The adapter pattern lets classes work together in spite of their incompatible interfaces.

The traditional way to implement this design pattern involves writing an adapter class and wrapping it around an existing class in order to create an interface comptabile with a third class. Ford's JRuby example for this design pattern dramatized the interface mismatch by showing how to determine when a square peg object could be forced into a round hole object.

Ruby allows a different solution. Since Ruby allows access to methods inside classes, you can insert a method inside one of the classes to resolve the interface mismatch. The adapter pattern can be implemented even more flexibly because the structure of a Ruby program can be changed when it is interpreted (it is a dynamic language). Therefore, two methods with the same name can exist within a class, and the decision about which one to use can be made when it is interpreted.

For those familiar with dynamic languages, a compilation step begins to seem like the equivalent of premature optimization. When your code is compiled into a static binary, no matter how efficiently it can execute, you can no longer change its runtime structure.

Dynamic Language Patterns

Finally, Ford introduced techniques he called Dynamic Language patterns. These patterns are the result of the capabilities of newer languages and could not have been anticipated when the Design Patterns book was written.

One design pattern involves the null object. It uses a special object representing null, instead of using an actual null. The result of using the null object should semantically be equivalent to doing nothing. The most dreaded error message in Java is java.lang.nullPointerException. The pattern that gives rise to the null pointer exception doesn't exist in Ruby: the NilClass is already defined. In Groovy, everything is an object except null. Therefore, to implement a null object design pattern in Groovy, you would define a null object so that objects with null properties can be used without generating exceptions.

Another design pattern for dynamic languages was dubbed "aridify" by Ford. It is inspired by the DRY acronym: Don't Repeat Yourself. Ford explained that ceremonious languages such as Java generate floods of repetition, whereas languages that emphasize essentials can eliminate much of this repetition.

Conclusion

In conclusion, Ford restated that traditional design patterns rely on structure to solve problems. In contrast, dynamic languages use language facilities to create simpler solutions. It helps to understand design patterns for what they are: descriptions of common problems. Given the new tools afforded by modern languages, we need to implement solutions that take advantage of them.

For More Information

» Neal Ford's Web Site
» Design Patterns: Elements of Reusable Object-Oriented Software
» "Design Patterns" for Dynamic Languages on the JVM (TS-4961)

 

Do you have comments about this article? We welcome your participation in our community. Please keep your comments civil and on point. You may optionally provide your email address to be notified of replies - your information is not used for any other purpose. By submitting a comment, you agree to these Terms of Use.