Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring ServiceLocator or pure factory pattern?

99% of my dependency is manage with DI pattern via @Autowired Spring annotation.

Nevertheless in a particular scenario, I can't determine which implementation to be used until run-time.

The most known case, is the multiple implementation of parsers.

The first solution is to used multiple @Autowired (ugly mode)

Interface Parser {
    <T> T parse();
}

@Component("JsonParser")
class JsonParser implements Parser {
    ...
}

@Component("XmlParser")
class XmlParser implements Parser {
    ...
}

class MyService {
    @Autowired
    @Qualifier("XmlParser")
    Parser xmlParser;

    @Autowired
    @Qualifier("JsonParser")
    Parser jsonParser;

    ...     
}

But if I have a large number of implementation that can be not acceptable.

The second solution is to used ServiceLocator from Spring

interface ParserServiceLocatorFactory {
    public Parser getParser(String parserName);
}

interface Parser {
    <T> T parse();
}

@Component("JsonParser")
class JsonParser implements Parser {
    ...
}

@Component("XmlParser")
class XmlParser implements Parser {
    ...
}

class MyService {
    @Autowired 
    ServiceFactory parserServiceLocatorFactory;

    void exampleMethod() {
        Parser xmlParser = parserServiceLocatorFactory.getParser("XmlParser");
    }
}

This way to do seems right to me but compared with the third solution?

The third solution is to used pure factory pattern and inject it.

@Component
public ParserFactory {
    Parser getParser(String parserName) {
        ...
    }
}

interface Parser {
    <T> T parse();
}

@Component("JsonParser")
class JsonParser implements Parser {
    ...
}

@Component("XmlParser")
class XmlParser implements Parser {
    ...
}

class MyService {
    @Autowired 
    ParserFactory parserFactory

    void exampleMethod() {
        Parser xmlParser = parserFactory.getParser("XmlParser");
    }
}

If you have pro/con for the previous solutions, or even better solution for my problem?

PS: it's pseudo code I may miss some small things :)

like image 693
Kakawait Avatar asked Sep 06 '13 09:09

Kakawait


People also ask

Does Spring boot use factory pattern?

With Spring Boot's powerful dependency injection, Factory pattern comes to the rescue when different sub types of objects are needed.

What is difference between dependency injection and factory design pattern?

Difference between Dependency Injection and Factory PatternIt requires both a dependent object and a factory object to work properly. DI makes the unit tests easier. It does not require boilerplate code. The factory pattern requires the object you want to test, the factory object, and the dependent object.

Which pattern is used in Spring boot?

In this tutorial, we'll look at four of the most common design patterns used in the Spring Framework: Singleton pattern. Factory Method pattern. Proxy pattern.

How does Spring achieve DI or IoC Service Locator pattern?

When we begin working with the Spring framework, we run into concepts such as Inversion of Control (IoC), and dependency injection (constructor, setter and field injection) as a way to achieve IoC. Additionally, Spring provides a form of IoC via the service locator pattern.


2 Answers

Below is an example of a service locator class. This returns a service for the given ID from the service registry. The registry is a MAP which is autowired as shown. This is a working example from a production system:

@Service
public class MyServiceLocator {

    @Autowired
    private Map<String, MyService> myServiceRegistry;

    /**
     * Service locator to find the right Domain service to interact with the      requested  data store
     *   
     * @param serviceID
     * @return   
     */
    public MyService locateServiceFor(String serviceID) {
        //Using the Given string 'serviceID' as key, lookup the service from the Registry
        return myServiceRegistry.get(serviceID);
}
like image 75
Kingz Avatar answered Oct 10 '22 20:10

Kingz


As an option you can use list injection:

public class SomeService {

    @Autowired 
    private List<Parser> parsers;

    public doSomethingWithParser(...) {
        ...
        Parser parser = getParser(JsonParser.class);
        parser.parse(...);
        ...
    }

    private Parser getParser(Class<Parser> targetClass) {
        Parser result = null;
        for(Parser parser : parsers) {
            if(parser.getClass().equals(targetClass)){
                result = parser;
            }
        }
        return transformer;
    }

}

Even better, you can add Parser.isCompatibleWith(SomeInput input) method to simplify paser detection code.

like image 33
Maksym Demidas Avatar answered Oct 10 '22 20:10

Maksym Demidas