Factory for everything - The Ossigeno Blog - Powered by Ossigeno 3.0_rc1

Posted by giorgio
On May 20, 2009 6:31:04 PM
factory.jpg
Why using a factory to produce simple objects? Because new keyword has to be isolated, even if it is only new Form();
We all know (or I hope so) that from a testability point of view, we should write classes that ask for dependencies in their constructor, and have factories that build the object and return a complete instance, ready to do the work. In the tipical php mvc application workflow, this is done in the controller (or in helpers).
What I have produced is action and view helpers that are created during the startup of the script and abstract away the creation of scaffolding forms; all done in the context of Zend Framework mvc implementation.
This approach is particularly useful as some business objects like repositories have dependency on the database connection and some other components: for instance the mailer that sends emails on new comments is required by their repository.
The descripted design is classic test-driven development: ask for dependencies in the constructor, so you can test the class in isolation by passing in mocks objects; then build factories that have methods for creating the top-level objects that you use in your script (or controllers, or views). We're not going to complicate it by talking about dependency injection containers.

Dive into the code
This can be an example of what I'm taliking about.

<?php
class My_CommentsRepository
{
    public function __construct(Doctrine_Connection $conn, My_Mailer $mailer)
    {
        // ...
}

class My_Factory
{
    public function createCommentsRepository()
    {
         return new My_CommentsRepository($this->_conn, new My_Mailer());
    }
}

Only top level objects need a factory method: supposing that I'm not using My_Mailer directly in my controllers or scripts, there is no need for a public method create it. You can consider a private one if it is used multiple times, or a lazy creation method in the factory that creates only one My_Mailer object to share between the multiple dependent objects.
Given that the factory is created at the startup, this kills singleton as only one instance of My_Mailer class is present in the application, but this uniqueness is controlled by the factory, as it is not a strict responsibility of My_Mailer. And it lets you test the mail sending on certain repository events by mocking out My_Mailer with PHPUnit.

What's new?
Now I get to the point of this article: what about an object with no dependencies?

<?php
class My_ScaffoldingForm
{
    public function __construct(Doctrine_Record $model)
    {
        // ...
}

My_ScaffoldingForm is an object that builds inputs and selects to reflect an active record object. The focus of this example is that its unique dependency - the record - is necessarily passed by the application level. There is no point in making a factory method that hides the Doctrine_Record instance as it is not factory responsibility to find the right model to edit.
So we can do something like this in our application script:

<?php
// find the model
$record = $someRepository->find($_GET['id']);
// instancing the form
$form = new My_ScaffoldingForm($record);

Wrong. What I suggest is to always wrap creation of an high level object in a factory method, also if the creation process is dumb as the class has no dependency or all the dependencies are not obvious and cannot be generated by an application-wide factory.

<?php
class My_Factory
{
     // ..other factory methods
    public createScaffoldingForm(Doctrine_Record $model)
    {
        return new My_ScaffoldingForm($record);
    }
}

// in the script or controller
$form = $myFactory->createScaffoldingForm($record);

Why? Because the point is in abstracting away the process, much like the Law of Demeter suggest to do with composed/aggregated object methods. If the constructor is going to change, just one line of code has to be edited to accomodate new depencies.
My_ScaffoldingForm (in reality it is named Otk_Form_Doctrine) was becoming larger and larger and as it touches 500 lines of code I felt the need to refactor away some of the responsibility (strategy for creation of form elements basing on column types and for the population of selects with options), and so the constructor become:

    public function __construct(Doctrine_Record $model, Otk_Form_Doctrine_Strategy $elementStrategy, Otk_Form_Doctrine_OptionsManager $manager)
    {
        // ...

but the new Otk_Form_Doctrine() was in all my controller and I had to grep it and substitute it with a call to a new factory method for the form. Imagine what happens if controllers using this scaffolding form were spread in various applications over the internet. This change would break compatibility with previous versions and it would be a disaster.
I have now learned that new not only must not be in models and library classes, but it should not be in controllers too. Put all your news in factories. It will soon pay.
Loading...