Use dependency injection.
Spring Framework is an extremely useful piece of software and my personal favorite but there are many alternatives such as Google Guice.
Using Spring, you would define two (three, fifteen, ...) separate contexts, one per language, and obtain needed component from appropriate context. It's similar to your second approach but without using Language
class. For example:
# English context: english.xml
<bean id="Tokenizer" class="EnglishTokenizer"/>
<bean id="Parser" class="EnglishParser"/>
...
# Your code
ApplicationContext englishContext = ...; // context itself is injected
Parser englishParser = (Parser) englishContext.getBean("Parser");
Another alternative is to have a single context but prefix your bean ids with your language, e.g. "English-Tokenizer" and "Chinese-Tokenizer".
If you've never used dependency injection before, this may sound like too much work for a result that can be achieved via factory and / or dynamic class loading :-) But it's not - and it can do so much more (you can configure properties / dependencies of your components; you don't have to worry about caching or maintaining your own singletons, etc...) that once you start using it you'll wonder how you've ever lived without it :-)
Update (answers questions in 2nd comment).
Here's a sample "ComponentLocator" pattern. ComponentLocator
is a singleton that has no dependencies on Spring. Its instance (and implementation) is injected by the context.
public abstract class ComponentLocator {
protected static ComponentLocator myInstance;
protected abstract <T> T locateComponent(Class<T> componentClass, String language);
public static <T> T getComponent(Class<T> componentClass, String language) {
return myInstance.locateComponent(componentClass, language);
}
}
Implementation of ComponentLocator
assumes beans in your context are named as their interface names followed by semicolon and language (e.g. "com.mypackage.Parser:English"). ComponentLocatorImpl must be declared as bean in your context (bean name doesn't matter).
public class ComponentLocatorImpl extends ComponentLocator
implements ApplicationContextAware {
private ApplicationContext myApplicationContext;
public void setApplicationContext(ApplicationContext context) {
myApplicationContext = context;
myInstance = this;
}
@Override
protected <T> T locateComponent(Class<T> componentClass, String language) {
String beanName = componentClass.getName() + ":" + language;
return componentClass.cast(myApplicationContext.getBean(beanName, componentClass));
}
}
In your code elsewhere (in main()?) you're going to load ApplicationContext
:
ApplicationContext ctx = new ClasspathXmlApplicationContext("components.xml");
Note that you don't actually need to refer to the context directly anywhere else in the application. Wherever you need to get your components you just do:
Parser englishParser = ComponentLocator.getComponent(Parser.class, "English");
Parser chineseParser = ComponentLocator.getComponent(Parser.class, "Chinese");
Note that the above is just one possible approach and it assumes that you're pretty much only putting your language-dependent classes in the context. In your case that's probably the best (due to requirement of having all languages available simultaneously) otherwise you'd be replicating all your classes (once per language), so your A/B/C question is probably not applicable here.
But if you do have A/B/C dependency what you can do is (I'm assuming A, B, C are interfaces and Aimpl, Bimpl, Cimpl are their implementations):
<bean id="A" class="Aimpl">
<property name="B" ref="B"/>
</bean>
<bean id="B" class="Bimpl">
<property name="C" ref="C"/>
</bean>
<bean id="C" class="Cimpl">
<property name="tokenizer" ref="Tokenizer:English"/>
</bean>
Your implementations would need to have setB(), setC() and setTokenizer() methods. This is easier then constructor injection, though latter is also possible.