views:

107

answers:

1

I am currently trying to figure out what the best way is to create my objects on my current PHP 5.2 project. I basicly have a Registry which returns objects by keys. If the Registry does not have an object with the specified key it will try to create one by calling a factory method that is supplied to the registry by construction. Look at the following code to calrify:

interface IFactory
{
    public function createProduct( $key );
}

class Registry
{
    private $m_elements = array( );
    private $m_factory;
    public function __construct( IFactory $factory )
    {
       $this->m_factory = $factory;
    }
    public function getElement( $key )
    {
      if ( !array_key_exists( $key, $this->m_elements ) )
      {
          $this->m_elements[$key] = $this->m_factory->createProduct( $key );
      }
      return $this->m_elements[$key];
    }
}

Because I have different of categories of objects I'd like to store in different registries I wrap a Registry oject into a singleton for each category, like this:

class SpecialRegistry
{
   private static $instance = null;

   public static function getInstance( )
   {
       if( self::$instance === null )
       {
           self::$instance = new Registry( new SpecialFactory( ) );
       }
       return self::$instance;
   }
}

My Special class is rather complex and big with alot of different attributes and composition objects. Because I do not want to bind my Special class to any specific backend I was going to have different Factories for each different backend (eg MySQL database or file stream). So I would move the loading and initialization logic to the Factories. This however will only work if all fields in the Special class are public to the Factory. They shouldn't however be public to the rest of the application.

In C++ I could use friends to circumvent this problem. Also here on stack overflow I read on nearly the same topic, but applied to C#. It said I should simply set all fields public in the Special class and have the Factory only return an Interface that exposes the methods and attributes I want to be public to the application. But since PHP does not support return type hinting I am not able to return only an interface that the Special class implements. Another way I came up with would be to actually have the SpecialFactory inherit from the Special class making it possible to access private and protected fields from the factory. This is what I called "Bastard Factory" since the factory inherits from it's own product.

I'd wish to know if anyone of you guys can come up with a better way of achieving what I want. Or am I completely off track and yielding the initialization process to the factory is an absolute no-go? I'm curious on your opinions!

+1  A: 

Coming from a Java background, how about adding a builder in there? This builder could expose its state via public fields which the factory can construct on. Once the builder is complete, pass it to your Special class and let it load its state from the builder.

Something like this in your factory (pseudocode):

public function createProduct() {
  SpecialBuilder builder = new SpecialBuilder();
  // Do whatever you would have done to the Special class to the builder
  // ...
  Special special = new Special(builder);
  return special;
}

In this way your Special object can still hide its internal state. The only real disadvantage I see is that it's a slight case of object overload.

Zecrates
I followed your suggestion, but adapting it a bit to PHP which allows multi dimensional associative arrays - which replace the SpecialBuilder pretty well. Thank you!
Daniel Baulig