Closures are coming to PHP

Dagfinn has a post looking at using the new closure feature of PHP 5.3. He compares using foreach for iteration versus array_map. “Interesting,” he concludes, “but not necessarily better than conventional alternatives.”

I agree for that case. Consider instead, a more complicated operation that requires a setup and a tear down after.

setup();
operation();
teardown();

Now what happens if we need to be able to customize operation? That’s common enough, one way of doing this is to create a template method.

 
class MyExample {
    function operation() {}
    function setup() {}
    function teardown() {}
    function doit() {
        $this->setup();
        $this->operation();
        $this->teardown();
    }
}

Now, we can subclass MyExample and override operation() with custom logic. This is well and good, but what the customization we need is fairly small. Creating a new class carries a certain weight. Especially if you are religious about one class per file.

 
class MyExampleExtension extends MyExample {
    function operation() {
        // custom logic
    }
}

Plus, you now have to deal with some creational patterns to make sure your custom class is used in the right context.

 
$myObject = $registery->get('MyExample');
$myObject->doit();

So, instead of encapsulating the pattern, its also very common to just copy and paste:

 
setup();
custom_operation1();
teardown();
//...
setup();
custom_operation2();
teardown();

But that’s not good on the duplicate code front. So here is an alternate implementation, but using a closure anonymous function as a callback.

 
class MyExample2 {
    function setup() {}
    function teardown() {}
    function doit($operation) {
        $this->setup();
        $operation();
        $this->teardown();
    }
}

The advantage of MyExample2 is that extending is that the setup and teardown pattern is encapsulated in one spot. You konw that if setup is called, teardown will also be called. But, extending the operation is very light weight.

 
$myObject = new MyExample2();
$myObject->doIt(function () { /* custom logic 1 */ });
// ...
$myObject->doIt(function () { /* custom logic 2 */ });

There is another significant benefit to this and that is locality of reference. Here, the custom1 logic and the custom2 logic appears in context, not far away in some custom class or function declaration. So you get encapsulation and reuse for the common code parts, but without the sprawl and overhead of declaring structures that will only be used once in a context far away their declaration.

Closures and anonymous functions decrease the activation energy to write good code.

That’s not to say that closures and anonymous functions can’t be abused. If you keep seeing the same logic over and over in an anonymous block, you should probably give it a name in the form of a class, method or function.

procata.com


One Comment

Leave a Reply