What is a factory and why FactoryLoader?
on March 22, 2008 @ 12:58 AM

This statement and question were posted to the http://www.gr-ruby.org mailing list as it pertained to the release of FactoryLoader. This post should answer the question “what is a factory?” and also provide more insight into what FactoryLoader’s role is.

1
2
> I've read over your website, but I still have a lagging question which
> completely blinds my ability to see the usefulness of it: What is a factory?

A factory is “a mechanism for encapsulating complex creation logic” [0].

Here’s an example. Let’s say that you start writing an application that is a project management tool for a business. To start with they need the ability to create Projects. This is no big deal, you have a ProjectsController#create action and it makes your Project by doing something similar to the below:

1
2
3
4
5
6
7
8
9
10
  def create
    project = Project.new params[:project]
    if project.save
      flash[:notice] = "successfully created the project"
      render :action => "create"
    else
      flash[:error] = "failed to create the project"
      render :action => "new"
    end
  end  

Now a few weeks later the customer says that in order to create a project it needs to be assigned a project manager off the bat (the currently logged in user). This is not a very big deal since you have a “current_user” already, you just update your controller to look like:

1
2
3
4
5
6
7
8
9
10
11
  def create
    project = Project.new params[:project]
    project.manager = current_user
    if project.save
      flash[:notice] = "successfully created the project"
      render :action => "create"
    else
      flash[:error] = "failed to create the project"
      render :action => "new"
    end
  end

A few more weeks go by and your customer says that projects are tied to company budgets and when they are created they need to be automatically assigned to the correct budget based on the type of project. So we update to ProjectsController#create looks like the below:

1
2
3
4
5
6
7
8
9
10
11
  def create
    project = Project.new params[:project]
    project.manager = current_user
    project.budget = Budget.find_by_project_type(project.type)
    if project.save
      flash[:notice] = "successfully created the project"
    else
      flash[:error] = "failed to create the project"
      render :action => "new"
    end
  end  

Uh oh. There are important business rules being applied in the controller. The controller’s responsibility is to map an incoming request with an outgoing response and to know high level what the application needs to be doing. It should not know the intimate details of Project creation. Since we’re implementing the business rules on the controller we can’t re-use them for anything, unless we make a POST request to ProjectsController#create. This is quite limiting.

Another option to make them more reusable would be to tuck this away in the Project model itself. Perhaps we add a before_save callback which finds the appropriate budget. Even then we’ve only moved the budget logic out of the controller and there is still requirement for a Project to have a manager. If we go this route we’ll be separating two important pieces of how a Project gets constructed from each other. We would have the ProjectsController#create action setting the manager and then a before_save callback on the Project model finding a budget, both of which are required for a Project to be constructed.

One thought to remedy this would be put everything in the Project model, maybe in a before_save callback or overriding the constructor. But we quickly hit a roadblock because our Project model doesn’t know about the current user, that is application state and only the model knows about that. We could do something stupid and make the current_user global to the application, but that is a bad decision with pretty bad repercussions.

It’d be nice if we had a single object responsible for constructing a Project with these business rules. This is where a ProjectFactory comes into play. The ProjectFactory looks like:

1
2
3
4
5
6
7
8
  class ProjectFactory
    def create(project_attrs, manager)
      project = Project.new project_attrs
      project.budget = Budget.find_by_project_type(project.type)
      project.save!
      project
    end
  end

And the ProjectsController#create action looks like:

1
2
3
4
5
6
7
  def create
    project = ProjectFactory.create params[:project], current_user
    flash[:notice] = "successfully created the project"
  rescue 
    flash[:error] = "failed to create the project"
    render :action => "new"    
  end

This is pretty straightforward. I just moved some of the lines from the action into the factory. More importantly then just moving line of code is that the act of constructing a valid project (given the customer’s requirements) isn’t the responsibility of the Project. The act of constructing a valid project is a process by itself which is important to the customer, so we isolate it in a ProjectFactory.

This is valuable because:

  • the business logic used to construct a project is in one spot. Should the customer change, add or remove requirements you only have to go to one spot to implement them. Granted if the method signature to this changes you have to go update the spots calling the create method, but you the actual logic for constructing the project isn’t spread throughout multiple files or classes.
  • it promotes reusability (the whole DRY thing), since my ProjectFactory is much more reusable then my ProjectsController.
  • it promotes single responsibility
  • it provides clearer meaning to the code base because we aren’t muddying up the controller or the Project model with creation logic
  • it makes for easier testing and easier to understand tests

The technical implementation of this pattern is mentioned in the GoF book [1]. The value of the pattern as it applies to Domain Driven Design is mentioned in the DDD book by Eric Evans.

Hopefully that helps clarify what a factory is, by seeing how it is used and some of the value it provides. This relates to the FactoryLoader because sometimes the progression of creation logic is over time.

For the first few weeks or months of a project you are hardcoding things like “Project.create” in multiple spots. When the customer starts introducing requirements for constructing (or updating) a Project you have to go update all of those spots with the same code (violating DRY) or go find all of those spots and refactor them to use a ProjectFactory, and then make a ProjectFactory. It doesn’t happen all the time, but when it does happen it can be time consuming, frustrating and painful to do.

What FactoryLoader would do in our example is it give us a ProjectFactory upfront w/o having to write any code. If we used FactoryLoader in the above example our ProjectsController#create action would look like:

1
2
3
4
5
6
7
8
9
10
  def create
    project = ProjectFactory.create params[:project]
    if project.save
      flash[:notice] = "successfully created the project"
      render :action => "create"
    else
      flash[:error] = "failed to create the project"
      render :action => "new"
    end
  end  

Where the ProjectFactory is created for you by FactoryLoader (you didn’t create the class, FactoryLoader did dynamically). There’s only one line that differs between the example that uses the FactoryLoader and the one that doesn’t.

Now when the customer starts adding business rules for creating a Project you can create your own ProjectFactory class and implement the customer’s business rules. FactoryLoader will see that you have provided your own ProjectFactory so it won’t dynamically create one. Since all of the spots where you create a Project are already using a ProjectFactory they get the changes for free. Overall you get to touch less code for the changes and you get to spend less time refactoring if any refactoring is needed.

The goal of FactoryLoader is to allow developers the ability to scale to these customer requirements with less pain. The above examples have been quite trivial, hopefully they are clear enough and meaningful enough to express why I wrote FactoryLoader,

— Zach Dennis http://www.continuousthinking.com

  • 0 – Domain Driven Design by Eric Evans
  • 1 – Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson and Vlissides
4 comments | Filed Under: factory_loader | read on

Factory Loader
on March 18, 2008 @ 06:10 AM

FactoryLoader is intended to help scale object creation with less pain and less refactoring by creating factory classes for basic object construction.

In the early stages of a project object creation is simple and dependencies are kept to a minimum. As the project grows so does the complexity of object creation and its dependencies. It doesn‘t make sense to create custom factory classes upfront to deal with complex object construction that may not exist yet. But when those custom factories are needed it is usually painful and time consuming to update the code base to use them. It‘s also easy for developers to give-in due to time constraints and start making bad decisions.

This is where FactoryLoader comes into play. It automatically creates a Factory class for your objects and provides a create method which passes any arguments along to your object‘s constructor.

When you need to have custom factory behavior you can implement the factory without having to update other code references (assuming you‘ve used the factory in the rest of your application rather then direct class references).

  project/
    init.rb
    lib/
     |--things/
            |-- foo.rb
            |-- bar.rb
     |--factories/
            |-- bar_factory.rb

Given the above project directory structure you could have the following code in init.rb:

1
2
 factory_loader = FactoryLoader.new("lib/factories")
 factory_loader.load("lib/things")

The first call constructs a factory loader telling it which directory is used to store developer-written custom factories.

The second call will create an in-memory factory class for each *.rb file in the lib/things/ directory. A FooFactory class will be created to correspond with the foo.rb file. The generated factory will provide a create method which will pass along all arguments to the constructor of the object it wraps. So…


 FooFactory.new.create :a => :b

is the same as:


 Foo.new :a => :b

Even though a bar.rb file exists a BarFactory will NOT be created. This is because we told the FactoryLoader that custom factories are storied in lib/factories/ and a bar_factory.rb file exists there, so FactoryLoader assumes you want to use a custom factory. It also assumes that the class inside of bar_factory.rb is BarFactory.

FactoryLoader dynamically creates the factory classes — they are not written to disk. FactoryLoader also uses file naming conventions to determine what to do. For example:

1
2
   foo.rb => FooFactory
   crazy_dolphins.rb => CrazyDolphinsFactory

Factory.new

The dynamically created factories are classes and create is an instance method on them. You have to construct a factory in order to use it. This is so the factories themselves can be easily used in dependency injection frameworks.

Making a custom factory

So you’re using FactoryLoader to do the work of creating factories for you. Let’s say our Foo object now has some creation logic that we do not want in Foo’s constructor. To put it in the FooFactory just create the file in lib/factories/foo_factory.rb. The contents of foo_factory.rb might look like:

1
2
3
4
5
6
7
8
9
class FooFactory  
  def create options={}
    if business_logic_passes?
      Foo.new options
    else
      raise "business logic failed"
    end
  end
end

Install it!

gem install -r factory_loader

More Info

Author

Me, Zach Dennis

Special Thanks

  • Dave Crosby at Atomic Object
  • Ryan Fogle at Atomic Object
0 comments | Filed Under: factory_loader | read on