Using Factories as Entry Points for APIs
Warning: file_get_contents(http://feeds.delicious.com/v2/json/urlinfo/data?url=http%3A%2F%2Fstephennimmo.com%2Fblog%2F2009%2F02%2F06%2Fusing-factories-as-entry-points-to-apis%2F) [function.file-get-contents]: failed to open stream: HTTP request failed! HTTP/1.0 999 Unable to process request at this time -- error 999 in /home1/stephenn/public_html/blog/wp-content/plugins/digg-digg/dd.class.php on line 866
How do we get started? What do we do first? Do we instantiate an object? Do we get our trusty Spring context started with a good old new ClassPathXmlApplicationContext? How can we have a centralized and easy to use entry point regardless of the environment or container?
The factory pattern is a great opportunity to allow the developer to manage access to objects and handle some of the service-type concerns such as dependency injection and context building.
public abstract class AbstractFactory implements ApplicationContextAware {
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(String contextFile) {
if (contextFile.indexOf("classpath") > 0){
this.applicationContext = new ClassPathXmlApplicationContext(contextFile);
} else {
this.applicationContext = new FileSystemXmlApplicationContext(contextFile);
}
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
By simply extending this AbstractFactory, your context will always be managed, regardless of whether you are running in a TestNG environment or in a web container with the ContextLoaderListener.
public class DomainObjectFactoryImpl extends AbstractFactory implements DomainObjectFactory {
public Person createPerson(){
return (Person) getApplicationContext().getBean("person");
}
}
Run it in your test...
@ContextConfiguration(locations={"classpath:DomainObjectContext.xml"})
public class TestDomainObjectFactory extends AbstractTestNGSpringContextTests {
private DomainObjectFactory domainObjectFactory;
@Test(groups = { "domain" })
public void testPerson(){
Person p = domainObjectFactory.createPerson();
assertNotNull(p);
}
public void setDomainObjectFactory(DomainObjectFactory domainObjectFactory) {
this.domainObjectFactory = domainObjectFactory;
}
}
If you create your modules to be packaged as supporting jars, such as when you use maven, you are able to include context files on the classpath using the classpath* searching features. To use this particular class, simply add the jar dependency to your maven pom and add in the import into your existing spring context.
At that point, the end user needs to know one thing. The name of the factory interface. If you name the context file as InterfaceContext.xml and the bean name is the same as the interface, no more searching for entry points. And if you get REALLY fancy the object type (or pattern) can even help you more.
Where do I find that one service? Service...ServiceFactory...ServiceFactoryContext.xml
What is the correct implementation for the Trade interface? What is a trade? It's a domain object. DomainObject. DomainObjectFactory. DomainObjectFactoryContext.xml
It can be this easy.