- A Different Kind of Duck Typing
- The Template Method Strikes Again
- Parameterized Factory Methods
- Classes Are Just Objects, Too
- Bad News: Your Program Hits the Big Time
- Bundles of Object Creation
- Classes Are Just Objects (Again)
- Leveraging the Name
- Using and Abusing the Factory Patterns
- Factory Patterns in the Wild
- Wrapping Up
Factory Patterns in the Wild
It is actually fairly difficult to find the classic inheritance-based versions of either factory pattern in the Ruby code base. Ruby programmers have resoundingly voted with their keyboards for the more dynamic versions of the factories, the ones based on the class objects or on various class naming schemes. For example, the SOAP library that came with your Ruby interpreter goes from XML strings to Ruby objects by leveraging the names. Similarly, the XMLRPC3 implementation that is included in the Ruby standard library supports several XML parsing options. Each of these methods of parsing XML has an associated parser class; there is one class for parsing the XML as a stream and another for parsing it as a DOM tree. But there is no separate subclass of some XMLRPC class for each parsing technique. Instead, the XMLRPC code simply holds on to the class of the chosen XML parser and manufactures new instances of the parser from the class object as required.
A fairly exotic version of a factory method lives in ActiveRecord. As we saw in Chapter 9, ActiveRecord has an adapter class for each different kind of database that it talks to—there is a MySQL adapter, an Oracle adapter, and so on. When you ask ActiveRecord to set up a connection to a database, you need to specify—along with the user name, password, and port—a string containing the name of the adapter that ActiveRecord should use. So you supply "mysql" if you want ActiveRecord to talk to a MySQL database or "oracle" if it is to communicate with Oracle. But how does ActiveRecord go from that adapter name to an instance of the adapter?
It turns out that ActiveRecord uses a fairly interesting technique to come up with an adapter instance, all of it centered on the Base4 class. Base starts out completely ignorant of any specific database adapters:
class Base # Lots of non-adapter-related code removed... end
However, each database adapter contains code that modifies Base in a particular way; that is, each adapter adds a method that creates its specific flavor of connection to the Base class. For example, the MySQL adapter contains code more or less like this:
class Base def self.mysql_connection(config) # Create and return a new MySQL connection, using # the user name, password, etc. stored in the # config hash... end end
Similarly, the Oracle adapter contains code that looks like this:
class Base def self.oracle_connection(config) # Create a new Oracle connection... end end
After all of the adapters are finished with it, the Base class has a method called <<db_type>>_connection for each of the different types of databases it can accommodate.
To create an actual connection from the name of the adapter, Base constructs the name of the database-specific method as a string. It works something like this:
adapter = "mysql" method_name = "#{adapter}_connection" Base.send(method_name, config)
The last line of this method effectively calls the database-specific method, passing along the database connection configuration (things like the database, user name, and password) as a parameter. Voila—instant connection.