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.
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?
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:
// 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.
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.
9 comments:
Hey, man.
I didn't find a link to download and test your class. Does it exist??
A couple of months ago, I was working on a class called "DocForms" that could generate forms based on doctrine models but I stopped it development when I had trouble with relationships.
My *experimental* work on generating form for Doctrine 1.x models is kept in the Ossigeno Cms subversion repository:
http://ossigeno.svn.sourceforge.net/viewvc/ossigeno/trunk/core/library/Otk/
The main point was distinguish between relationship towards entities, which use a select or multiselect, and relationship towards value objects which use Zend_SubForms.
It's an open source product, so feel free to do nearly whatever you want with it :)
Is there anyway of making form generation standalone??
My solution relies on zend framework Zend_Form component which is highly coupled to Zend_View and other components to generate the html.
Hi Giorgio,
So the idea here is that inside My_Factory, the method createScaffoldingForm would look something like this:
public function createScaffoldingForm
(Doctrine_Record $model)
{
$strategy = new Otk_Form_Doctrine_Strategy();
$manager = new Otk_Form_Doctrine_OptionsManager();
$form = new Otk_Form_Doctrine($model, $strategy, $manager);
return $form;
}
Then calling code - like an action controller - would use:
$form = $myFactory->createScaffoldingForm ($model)
Is that right?
So controller code only sees the factory method, while inside the factory, you can create helper strategies, and managers, and gerbils on wheels. Client code is not aware.
Is that right?
Yes, I'm in a hurry now but it seems all right. If client code is in an object, it is not even aware of what factory is if you pass the factory in the constructor of your client (see Abstract Factory pattern). The benefits of this approach are independent testing of all the classes (besides the factory that contains only new operators and no logic to test).
So is the idea that there will be one factory for the app that performs lots of tasks like this?
This single factory functions as a kind-of DI container that creates/stores instances of my various services.
Any client class that needs these services is passed the factory/container (probably in its constructor).
Does that sound right?
But then doesn't this factory/container get huge, encompassing all "service" creation?
I understand the benefit of DI, but I always seem to get tripped up on notions of:
1. Scope: where in my execution flow is my DI container actually instantiated),
2. who gets passed what, and how much responsibility it should have. Should all clients classes accept my DI container as a constructor parameter, so all clients classes need to be DI-aware?
Or should client constructors only receive the services that they need? In this case, the signature of the constructor is susceptible to change as we abstract/refactor service responsibilities.
3. Size: if my factory/container is responsible for all my service creation, won't it get huge and unwieldy over time?
Sorry for all the questions, but I feel like I am right on the edge of "getting" a lot of these compositional, loosely-coupled, DI concepts. But not yet quite there.
Thanks and cheers,
Of course you should ask! Discussion on this practices is needed.
A DI container is in fact an automatically generated factory, but I prefer abstract factories since they are easier to configure.
Of course the main factory will be huge, but it contains only *new* operators so it's not a big problem. Maybe 100 lines of *new*s?
Beware that client objects require in the constructor only the collaborator, and not a factory. The factory instance the collaborators and injects them. No one has knowledge that exist a main factory. Decoupling from the collaborators *interfaces* is not possible as the client class depends on them.
I wrote about factories here:
http://giorgiosironi.blogspot.com/2009/10/object-oriented-myths.html
What a great web log. I spend hours on the net reading blogs, about tons of various subjects. I have to first of all give praise to whoever created your theme and second of all to you for writing what i can only describe as an fabulous article. I honestly believe there is a skill to writing articles that only very few posses and honestly you got it. The combining of demonstrative and upper-class content is by all odds super rare with the astronomic amount of blogs on the cyberspace.
Post a Comment