by Miško Hevery
I have already hinted that your code should either be in the business of doing business logic or in the business of instantiating and wiring other objects together. What this effectively means is that your either have classes with ifs and loops (your business logic) or you have classes with new operators (your factories). Now at this point many people assume that they will need a factory for each class which will double the number of classes. But in practice you only need one factory class each time you cross an object lifetime. For example most web application, this implies that you have a factory for the application lifetime objects and than a factory for all af the request lifetime objects. More specifically one request lifetime factory for each servlet we have, since a servlet is long lived and we need a request lived object (we are crossing the boundary). Let’s look at an example of a LoginServlet.
The problem with the LoginServlet is that it is of wrong lifetime. A servlet is application lifetime object or a singleton. This means that we can not store any request level information in the servlet. Most people solve this problem by passing all of the request lifetime information through the stack to all of the different methods. However I thing that the responsibility of the servlet is to create a request lifetime object graph and than pass the execution to it. By creating a request scope object graph we are free to store the data in the class fields rather than being forced to pass them around on the stack. The responsibility of the LoginServlet is to create a LoginPage, it is a factory.
To allow the removal of the new operators from our business logic classes, we need to ask for our collaborators instead of making them. This means that each class only knows about other classes with which it is in direct contact and which it needs to get its job done. In our example the LoginPage would ask for AuditRecord and Authenticator in its constructor. Similarly the Auhenticator class would ask for Database, Captcha and AuditRecord as its collaborators. Finally the Database would ask for AuditRecord.
But how do we get the right information to the right location, or to put it differently, how do I get information to the database object without the Authenticator knowing, since the Authenticator is in the middle? The trick lies that the view of the world from the factory is different than the view from the collaborator.
Each collaborator only knows about its direct dependencies, as depicted by the arrow pointing to the right. And that is a good thing since it makes code reusable and it prevents any one class from knowing too much.
Now, let’s look at the factory. When the factory calls a new on the Login Page it must satisfy two dependencies: AuditRecord and the Authenticator. To make an Authenticator we must first instantiate a Database, Captcha and AuditRecord. However, we need to make sure that the AuditRecord in the Authenticator and in LoginPage is same instance, so we simply reuse it. Here is how it looks like in the code form:
AuditRecord audit = new AuditRecord(); Database database = new Database(audit); Captcha captcha = new Captcha(); Authenticator authenticator = new Authenticator(database, captcha, audit); LoginPage = new LoginPage(audit, authenticator);
What I find interesting is how different the code looks from the two points of view. The collaborators only know about its direct dependecies and the factories know about everything, and that is a good thing. Because the factories know about all of the objects in the graph they can easily make sure that everyone gets the same instance of the AuditRecord. In other words the factory can easily enforce that AuditRecord is a singleton without having to rely on a global variable. It also means that the factory can deliver the information to any one object without anyone else knowing about the information unnecessarily. For example if the AuditRecord needs to know the the HTTP cookie than the factory can inject the cookie into the AuditRecord without effecting any of the layers in between, namely the Authenticator and Database.