This is the second part of the post Object-oriented myths. You may want to read the first part before going forward.
In the last post we were talking about the advantages of encapsulation, Dependency Injection and factories. Let's dig in other legends about object-oriented programming and how it makes you write boring and difficult to understand code (clue: it is not true).
''But I will need a Factory for every object. What a pain to write all these classes.''
No, this is not true, you will often need at most two or three factories. I have seen Misko Hevery answers this question multiple times in his blog and talks.
First, simple classes like entities, without collaborators, do not need factories. Second, the same factory which builds the application (at every http request if you are using a shared-nothing architecture such as php's one), builds everything with a lifetime equal to the request one. All these objects such as front controllers, loaders and so on are built from the same factory and injected as collaborators when you call MainFactory::createApplication().
Maybe you need other objects with a shorter lifetime: for instance consider view helpers for web applications, which are used for creating html code that would be boring to write by hand. Their creation can be encapsulated in a ViewHelperFactory class since it's a waste to create 50 helpers at every request when only a handful of them may be used. So an instance of ViewHelperFactory is built in MainFactory and then injected into the view object that renders the page. Helpers are thus created as needed.
This identical multiple-lifetime pattern is reflected in Java when dealing with Session-wide objects and Request-wide objects, the formers being built at the startup and the latters with a RequestFactory. You can have how many levels you want, for instance a MainFactory/SessionFactory (the principal one of the application) can create a RequestFactory which can in turn create a ViewHelperFactory, all these creations at different moments in execution. In php, though, everything is garbage collected at the end of the request so there's one less level.
''I should use a Singleton and implementing in it lazy loading, so that I make sure there is only one instance of my database connection, and only if it's used.''
What is the responsibility of your PDO/Zend_Db/My_Db_Adapter/Connection instance? Bridging your code with the database. It probably needs a QueryFactory if you create query objects by calling it, but it's not the point of this paragraph. So the responsibility of the db object is not to control that there is only one instance of it and nor it is to lazy load itself. This responsibility should be in its factory, which will cache the instance to perform lazy loading and ensure only one connection is created. Following the previous example, MainFactory will pass the same instance of DbFactory to all your classes that needs it, and the connection will be shared and conserved as a DbFactory private field. Consider also to just pass in the db object if you do not really need lazy connecting capabilities.
If you do not multiply the instances of a Factory, it can perform lazy loading and Singleton-like behavior, without obscuring your design with global state. And you can easily avoid duplication of factories by creating and kepting them in the MainFactory of your application. Using a Singleton is a symptom of unresolved dependency, since a Client class, with empty constructor and no setters, can reference a singleton, lying about what it needs to work correctly and causing debugging nightmares when an hidden collaborator breaks. You'll find out while testing, reusing and reading this type of code that it causes great pain: it's not an oop fault, but thoughtlessness of who designed the Singleton.
''Let's make an Utility static class where we can put all these methods. They seem to not have a place to live in.''
Using an Utility class is a smell that you have code which should belong to already existent classes. You should consider to put these methods in the objects which they work with, or to create services to give this code a home if they do not fit in an already existent class. In these service classes you can put cohesive sets of methods which depends on the same collaborators, instead of creating a big static Utility class where you sink all code that does not make sense elsewhere, and which becomes a concentrate of dependencies, being impossible to mock out. A service is passed only in the classes that need it instead.
I hope I have contaminated you with real, confident object-oriented programming ideas. Oop has a learning curve but it should not be difficult in the long run: it is a tool created to simplify the programmer's life and not to make him experience pain and boredom.
This post's title is an homage to The Rest of the Robots. Asimov is one of the few science fiction authors that keep me nailed to a book.
Nice and helpful. Thank's for "object-oriented myths" series.
ReplyDeleteHow would you deal with the following situation. I need to convert a string myarray[index][1] into an actual array. Now in Zend_Form, there is a protected method which does just that. So my choices are either copy that method into my class, put it in my class as an anonymous function, make a global function, a "utility" class, or a StringToArray service object?
ReplyDelete