Real-life closures examples ...for real
Join the DZone community and get the full member experience.
Join For Free$newArray = array();with anonymous functions/closures:
foreach ($array as $value) {
$newArray = $value * $value;
}
$newArray = array_map($array, function($value) {
return $value * $value;
}
apart from explaining the new constructs of PHP 5.3 (thus, the syntax of closures), or making the average developer scratching his head trying to reconstruct the flow? Ah, yes, you save 1 line of code and make php.net/array_map do well in the site analytics.
I was killing foreach cycles too
I wrote an article long time ago about array_map(), array_filter(), and they are very handy in some cases, but I discovered there is a catch: they do their best when you already have a defined function to pass them.
$newArray = array_map($array, 'sqrt');
// or, with the strange callback syntax:
$newArray = array_map($array, array($object, 'methodName'));
Otherwise, if you have to define a specific anonymous function to wrap the content of the foreach() cycle, which may be a single line, array_*() functions are totally equivalent to foreach in expressivity, ease of use, and usually lines of code to maintain. You can say that extracting the function is a useful abstraction, and in fact this is also an acceptable solution:
$newArray = array();
foreach($array as $value) {
$newArray = $this->square($value);
}
It's only different style, and you should favor consistency in your codebases over debating whether array_map() will improve. You can use anonymous functions and closures in place of foreach(), it's cool, but you gain almost nothing.
There's one field where there are advantages in specifying map and filtering functions explicitly, and pass them to constructs like array_map(): when these constructs do more than a simple cycle, like parallelization. In CouchDB for instance, database views are specified as a couple of map() and reduce(), which can be executed in parallel over a cluster of machines and only on changed data for view update. A foreach() over a PHP array is not what comes to mind when thinking about MapReduce.
Show us the code
So here's my real real-life example for closures: in my opinion, they are not meant for substituting foreach(), but for example to pass a curried callback method around (an implementation of the Command pattern).
Note that the real world example reported here is not about real world code: it has been translated into a more mundane computation of classes representing physical objects. The Command pattern implementation is from the real world, the Domain Model of an application I'm working on; while the particular classes obviously aren't, so that you can concentrate on understanding the pattern and not my domain of choice.
<?php
/**
* I like real world objects for real world examples. :)
*/
class Car
{
private $tiresPressure = 0;
public function inflateTires(Pump $pump)
{
$this->tiresPressure += $pump->inflation();
}
public function __toString() { return "Car with tires pressure $this->tiresPressure"; }
// many other methods...
}
class Pump
{
/**
* @return int the pressure this Pump augments with a single usage
*/
public function inflation()
{
return 0.1;
}
}
// client code
$car = new Car;
$pump = new Pump;
// now we have the pass Car and Pump to another object, which may call inflateTires() at the time of its choosing
// this is specifically the case in which we are unable to inject Pump in Car, since it would be used only in that method, or because Car is an Entity and our ORM does not allow us to inject service in it
// passing both Car and Pump to who needs them does not scale: imagine if the parameters were 2 or 3 instead of the single Pump
// so, we use the Command pattern (GoF book) to hide Pump and Car behind a single object
$command = function() use ($car, $pump) {
return $car->inflateTires($pump);
};
$command();
echo $car, "\n";
// the "goodness" here is that we do not need to define other classes, we can quickly create this Command object
// sometimes the object can create the Command by itself
class Truck
{
private $tiresPressure = 0;
public function inflateTires(Pump $pump)
{
$this->tiresPressure += $pump->inflation();
}
public function __toString() { return "Truck with tires pressure $this->tiresPressure"; }
/**
* Loosely known as Extension Object, but it's still a specialized Command.
*/
public function tiresInflationCommand(Pump $pump)
{
// $this is reserved, so we have to
$self = $this;
return function() use ($self, $pump) {
return $self->inflateTires($pump);
};
}
// many other methods...
}
$truck = new Truck();
$command = $truck->tiresInflationCommand($pump);
$command();
echo $truck, "\n";
Opinions expressed by DZone contributors are their own.
Trending
-
Structured Logging
-
A Data-Driven Approach to Application Modernization
-
How to LINQ Between Java and SQL With JPAStreamer
-
Security Challenges for Microservice Applications in Multi-Cloud Environments
Comments