views:

156

answers:

3

I've always worry about calling methods by referencing them via strings.

Basically in my current scenario, I use static data mapper methods to create and return an array of data model objects (eg. SomeDataMapper::getAll(1234)). Models follow the Active Record design pattern. In some cases, there could be hundreds of records returned, and I don't want to put everything into memory all at once. So, I am using an Iterator to page through the records, as follows

$Iterator = new DataMapperIterator('SomeDataMapper', 'getAll', array(1234));

while ($Iterator->hasNext()) {
  $data = $Iterator->next();
}

Is that a good way of doing this? Is it a bad idea to pass as strings the name of the mapper class and the method? I worry that this idea is not portable to other languages. Is this generally true for languages like Ruby and Python? If so, can anyone recommend a good alternative?

FYI, for future peoples' refernce, I call the method like this:

$method = new ReflectionMethod($className, $methodName);
$returnValue = $method->invokeArgs(null, $parameters);
+1  A: 

It is an acceptable way of doing it. Both Python and Ruby support it and thus should be portable. Python can do it as easily as PHP can, however Ruby has a little more to it. In Python at least, it is useful for when the particular class you're referencing has not yet been imported nor seen yet in the file (i.e. the class is found lower in the same file as where you're trying to reference it.)

Getting a class object from a string in Ruby: http://infovore.org/archives/2006/08/02/getting-a-class-object-in-ruby-from-a-string-containing-that-classes-name/

AlbertoPL
Good to know. Thanks!
Chad Johnson
+1  A: 

PHP doesn't really support the passing of functions any other way. All dynamic method invocation functions in PHP take what they call a "callback" - see http://us.php.net/manual/en/language.pseudo-types.php#language.types.callback for documentation on that. As you'll see, they're just string or arrays of strings in different usage patterns, so you're not far off.

There are however, design patterns that work around this. For instance, you could define a DataMapper interface that all of your mapper classes must implement. Then, instead of passing in the class and method as string, you could pass the mapper instance to your iterator and since it requires the interface it could call the interface methods directly.

pseudocode:

interface DataMapper
{
   public function mapData($data);
}

class DataMapperIterator ...
{
  public function __construct(DataMapper $mapper, ...)
  {
   ...
  }
  ...
  public function next()
  {
    ... now we can call the method explicitly because of interface ...
    $this->mapper->mapData($data);
  }
}

class DataMapperImplemenation implements DataMapper
{
  ...
  public function mapData($data)
  {
    ...
  }
 ...
}

Calling methods by name with passed in strings isn't horrible, there's probably only a performance penalty in that the bytecode generated can't be as optimized - there will always be a symbol lookup - but I doubt you'll notice this much.

donovan Jimenez
Good points, and I would like to do this, but my mapper's methods are static. Also, I have different mapper methods for different types of results sets...like ::getById(), getByGroupId(), etc.
Chad Johnson
+3  A: 

This is essentially a version of the factory pattern - Using strings to create a object instance.

However, I question the design idea of using an iterator to control the paging of data - that's not really the purpose of an iterator. Unless we just have name confusion, but I'd probably prefer to see something like this.

$pager = new DataMapperPager( 'SomeDataMapper', 'someMethod', array(1234) );
$pager->setPageNum( 1 );
$pager->setPageSize( 10 );
$rows = $pager->getResults();

foreach ( $rows as $row )
{
   // whatever
}

Of course, DataMapperPager::getResults() could return an iterator or whatever you'd want.

Peter Bailey
Good catch. I think I'll rename this to *Pager and then make it return an Iterator as you suggested.
Chad Johnson
I actually ended up implementing PHP's Iterator interface so that I can use this class with foreach.
Chad Johnson