Although Spring, in
itself, is already an example implementation of the Factory pattern (any Spring
application's application context is just a giant Factory, right!), from time
to time, we would like to implement this same pattern in our application logic
just to keep our code clean and tidy. Let's see how we can do this, by a short
example:
1 Defining an interface for the classes to be
instantiated through the factory:-
Let's say we're working
on a Spring application that handles document printing. For a given document,
it is able to print it in either A4 or A5 format or either portrait and
landscape layout. Each of these printing strategies extends a common interface:
package strategy;
import model.Document;
public interface IPrintStrategy {
public void print(Document
document);
}
Here are the concrete implementations for each
printing strategy. To keep this example simple, each concrete printing strategy
will only indicates what it is supposed to:
package strategy;
import model.Document;
import org.springframework.stereotype.Component;
@Component("A4Landscape")
public class PrintA4LandscapeStrategy implements IPrintStrategy{
@Override
public void print(Document document) {
System.out.println("Doing stuff to print an
A4 landscape document");
}
}
package strategy;
import model.Document;
import org.springframework.stereotype.Component;
@Component("A5Landscape")
public class PrintA5LandscapeStrategy implements IPrintStrategy{
@Override
public void print(Document document) {
System.out.println("Doing stuff to print an
A5 landscape document");
}
}
package strategy;
import model.Document;
import org.springframework.stereotype.Component;
@Component("A4Portrait")
public class PrintA4PortraitStrategy implements IPrintStrategy{
@Override
public void print(Document document) {
System.out.println("Doing stuff to print an
A4 portrait document");
}
}
package strategy;
import model.Document;
import org.springframework.stereotype.Component;
@Component("A5Portrait")
public class PrintA5PortraitStrategy implements IPrintStrategy{
@Override
public void print(Document document) {
System.out.println("Doing stuff to print an
A5 portrait document");
}
}
For now, just note that
each printing strategy interface is annotated as being a Spring component with
a certain name.
2 Defining the factory class interface:-
Now, we'll have to
define an interface that is to be implemented by the factory class through
which we will retrieve printing strategies:
package strategy;
public interface PrintStrategyFactory {
IPrintStrategy
getStrategy(String strategyName);
}
As you can see, the
factory class will get a strategy name as input and will return an instance of
IPrintStrategy.
3 Defining the Spring application configuration
<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="strategy">
<bean class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean" id="printStrategyFactory">
<property name="serviceLocatorInterface" value="strategy.PrintStrategyFactory">
</property></bean>
<alias alias="A4P" name="A4Portrait">
<alias alias="A4L" name="A4Landscape">
<alias alias="A5P" name="A5Portrait">
<alias alias="A5L" name="A5Landscape">
<alias alias="DEFAULT" name="A4Portrait">
</alias></alias></alias></alias></alias></context:component-scan></beans>
In this configuration file:
·
we specify the package
to scan for spring component detection (here, it's the "strategy"
package)
·
we declaratively define
a bean of type "ServiceLocatorFactoryBean" that will be the Factory.
·
we also define, and
that's optional, name aliases, so
that a same printing strategy could be retrieved either by the name specified
in the @Component annotation, or by the alias.
4 Testing our example
With the following test
class, we can check that everything works as expected:
import model.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;
import strategy.PrintStrategyFactory;
@ContextConfiguration(locations
= {"classpath:/spring-config.xml"})
public class SpringFactoryPatternTest extends AbstractTestNGSpringContextTests{
@Autowired
private PrintStrategyFactory
printStrategyFactory;
@Test
public void printStrategyFactoryTest(){
Document doc = new Document();
printStrategyFactory.getStrategy("DEFAULT").print(doc);
printStrategyFactory.getStrategy("A5L").print(doc);
printStrategyFactory.getStrategy("A5P").print(doc);
printStrategyFactory.getStrategy("A5Portrait").print(doc);
}
}
For instance, when we
ask the factory class to give us the printing strategy answering to the name
"DEFAULT", we'll end up with an instance of
"PrintA4PortraitStrategy" as "DEFAULT" is an alias
for the spring component with name "A4Portrait".
From there, I believe
you got how it works. If not, feel free to checkout the source code and execute
the test class, and you'll grasp the whole concept in the blink of an eye.
No comments:
Post a Comment