Applying the Service Locator Pattern to Decouple File Parsers in Java
This article explains how to replace tightly‑coupled if‑else or switch‑case logic for selecting file parsers (JSON, CSV, XML) with a Service Locator Pattern using Spring's ServiceLocatorFactoryBean, demonstrating a clean, extensible solution that follows the Open/Closed principle.
Hello everyone, I’m Chen. In many projects we select different implementations (e.g., CSV or JSON parser) based on a type using if else or switch case , which tightly couples the client to concrete classes. This article introduces the Service Locator Pattern to eliminate that coupling.
File Parser Example
We illustrate the pattern with a simple example that parses files of different formats.
Assume an application needs to parse CSV and JSON files.
1. Define an enum for content types:
public enum ContentType {
JSON,
CSV
}2. Define a parser interface:
public interface Parser {
List parse(Reader r);
}3. Provide concrete implementations:
// CSV parser
@Component
public class CSVParser implements Parser {
@Override
public List parse(Reader r) { .. }
}
// JSON parser
@Component
public class JSONParser implements Parser {
@Override
public List parse(Reader r) { .. }
}4. A naïve client would use a switch case to select the parser:
@Service
public class Client {
private Parser csvParser, jsonParser;
@Autowired
public Client(Parser csvParser, Parser jsonParser) {
this.csvParser = csvParser;
this.jsonParser = jsonParser;
}
public List getAll(ContentType contentType) {
switch (contentType) {
case CSV: return csvParser.parse(reader);
case JSON: return jsonParser.parse(reader);
// ...
}
}
}This approach works but requires modifying the client whenever a new type (e.g., XML) is added, violating the Open/Closed principle.
Applying the Service Locator Pattern
We introduce a service locator interface ParserFactory that returns a Parser based on ContentType :
public interface ParserFactory {
Parser getParser(ContentType contentType);
}Configure Spring's ServiceLocatorFactoryBean to create the factory:
@Configuration
public class ParserConfig {
@Bean("parserFactory")
public FactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
// set the service‑locator interface
factoryBean.setServiceLocatorInterface(ParserFactory.class);
return factoryBean;
}
}Register each parser bean with a name matching the enum constant:
// Bean name matches enum value
@Component("CSV")
public class CSVParser implements Parser { .. }
@Component("JSON")
public class JSONParser implements Parser { .. }
@Component("XML")
public class XMLParser implements Parser { .. }Update the enum to include XML:
public enum ContentType {
JSON,
CSV,
XML
}Now the client can obtain the appropriate parser without a switch:
@Service
public class Client {
private ParserFactory parserFactory;
@Autowired
public Client(ParserFactory parserFactory) {
this.parserFactory = parserFactory;
}
public List getAll(ContentType contentType) {
// Directly retrieve the parser from the factory
return parserFactory.getParser(contentType).parse(reader);
}
}Adding a new file type only requires creating a new parser bean and adding the enum constant; the client remains unchanged, satisfying the Open/Closed principle.
Recommended Java Engineer Guide: https://github.com/chenjiabing666/JavaFamily
If you find using the type name as the bean name odd, you can use a constant‑based approach:
public enum ContentType {
JSON(TypeConstants.JSON_PARSER),
CSV(TypeConstants.CSV_PARSER),
XML(TypeConstants.XML_PARSER);
private final String parserName;
ContentType(String parserName) { this.parserName = parserName; }
@Override public String toString() { return parserName; }
public interface TypeConstants {
String CSV_PARSER = "csvParser";
String JSON_PARSER = "jsonParser";
String XML_PARSER = "xmlParser";
}
}
@Component(TypeConstants.CSV_PARSER)
public class CSVParser implements Parser { .. }
@Component(TypeConstants.JSON_PARSER)
public class JSONParser implements Parser { .. }
@Component(TypeConstants.XML_PARSER)
public class XMLParser implements Parser { .. }Analysis of the Service Locator Pattern
The pattern removes the client’s direct dependency on concrete implementations. As Martin Fowler notes, a service locator knows how to obtain any service the application may need, providing a method that returns the required service on demand.
Spring’s ServiceLocatorFactoryBean implements FactoryBean to create a service‑factory bean that can locate services at runtime.
Conclusion
Using the Service Locator Pattern offers a flexible way to extend Spring’s inversion of control for cases where dependency injection alone is insufficient, though DI remains the preferred approach in most scenarios.
Final Note
If this article helped you, please like, share, and follow the author. Your support keeps the content coming!
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.