Arturo Herrero

Closure Design Patterns

Design Patterns

These days I’m learning design patterns. There are a lots of documentation about software design patterns, but I’m interesting in closure design patterns.*

Many patterns imply object-orientation, so may not be as applicable in dynamic languages. Peter Norvig demonstrates that 16 out of 23 patterns in the Design Patterns book are simplified or eliminated, Design Patterns in Dynamic Languages.

I’ve found an interesting presentation of Venkat Subramaniam about Design Patterns in Java and Groovy and another presentation of Neal Ford about Design Patterns in Dynamic Languages. Here, I extract some patterns of these presentations that involve closures and add others patterns based on my own experience.

You can also find the same examples for Ruby here: Closure Design Patterns. Ruby Edition.

* Groovy makes no such distinction between closures or anonymous functions. What I’m really trying to get at is how we can use tools such as first-class functions, lambdas and closures when implementing design patterns.

Closure Design Patterns

Execute Around Method

A pair of operation that needs to be performed before and after operations.

def operations(closure) {
    println "Open"
    closure()
    println "Close"
}

operations { println "Operation" }

===> Open
===> Operation
===> Close

Pluggable Behavior

Specify the behavior of an object at runtime.

def selectValues(number, closure) {
    def list = []
    1.upto(number) {
        if (closure(it)) list << it
    }
    return list
}

assert [2, 4, 6, 8, 10] == selectValues(10) { it % 2 == 0 }  // even
assert [1, 3, 5, 7, 9]  == selectValues(10) { it % 2 != 0 }  // odd

Iterator Pattern

Allows sequential access to the elements.

def listNumbers(closure) {
    (0..3).each { closure it }
}

listNumbers {
    if (it < 2) println "$it is a little number"
    else println "$it is a big number"
}

===> 0 is a little number
===> 1 is a little number
===> 2 is a big number
===> 3 is a big number

Dynamical Conditional Execution

Create and execute a conditional operation.

def greet(user, successClosure, failClosure) {
    if (isAdmin(user)) successClosure()
    else failClosure()
}

greet(user, { println "Hi Admin!" }, { println "Hello User" })

Template Method Pattern

Define common algorithm steps (getting a customer) and customizations (passed as a closure).

def withCustomer(id, closure) {
    def customer = getCustomer(id)
    closure(customer)
}

withCustomer(1234) { customer ->
    println "Found customer $customer.name"
}

Loan Pattern

Ensures that a resource is deterministically disposed of once it goes out of scope.

def withListOfWordsForEachLine(file, closure) {
    def reader = file.newReader()
    try {
        reader.splitEachLine(' ', closure)
    } finally {
        reader?.close()
    }
}

withListOfWordsForEachLine(file) { wordList ->
    println wordList
}

Command Design Pattern

Encapsulate all the information needed to call a method at a later time.

def count = 0
def commands = []

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

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

===> count is initially 0
===> did all commands, count is 10

Strategy Pattern

Define a family of interchangeable algorithms.

calcMult = { n, m -> n * m }
calcAdds = { n, m ->
    def result = 0
    n.times { result += m }
    return result
}

def calcStrategies = [calcMult, calcAdds]
calcStrategies.each { calc ->
    assert 10 == calc(5, 2)
}

Factory Pattern

Abstract the object creation process (currying as a function factory).

def adder = { x, y -> x + y }
def incrementer = adder.curry(1)

assert 5 == incrementer(4)

Method Combination

Build a method from components.

def sum = { Collection collection -> collection.sum() }
def first2 = { Collection collection -> collection.take(2) }
def take2andAdd = first2 >> sum

assert 3 == take2andAdd([1, 2, 3, 4, 5])

April 25, 2012 | @ArturoHerrero