java - 多语言组件/类 [OOP/Patterns/IoC/DI]

标签 java design-patterns oop architecture dependency-injection

我有几个组件存在不同的版本,具体取决于系统使用的语言(可配置,并且可以在运行时更改)。例如,我有一个用于 Tokenizer 的接口(interface)(“组件”),以及两个用于英语和中文的具体实现,如下所示:

public interface Tokenizer {
    List<String> tokenize(String s);
}

public class EnglishTokenizer implements Tokenizer {
    List<String> tokenize(String s) { ... };
}

public interface ChineseTokenizer implements Tokenizer {
    List<String> tokenize(String s) { ... };
}

现在,在我的代码的许多类中,我需要获得其中一些组件(TokenizerParser 和许多其他组件)的特定于语言的实现,并且我想知道实现此目标的最优雅方法是什么?我想到了使用以下方法之一:

  • 每个组件(例如 Tokenizer)都会有一个工厂(单例),给定一个语言 enum,它将返回适当的语言特定实现,像这样(这需要许多工厂):

    public enum TokenizerFactory {
        SINGLETON;
        private Map<Language, Tokenizer> cache;
        public getTokenizer(Language) {
            return cache.get(Language); 
        }
    }
    
  • 有一个(相当大的)Language 类,它将用特定语言 enum 实例化,并且会有许多不同的方法来获取特定语言成分。然后,在运行时,我可以轻松地在语言之间切换(这是我的目标之一)。像这样:

    public class Language {
        public Language(LanguageEnum) {/* load language specific components*/};
        public Tokenizer getTokenizer() {/* language specific tokenizer */};
        public Parser getParser() {/* language specific parser */};
    }
    

实现我想要做的事情的最合适的方法是什么?我怎样才能改进我的代码?

最佳答案

使用dependency injection .

Spring Framework是一款非常有用的软件,也是我个人的最爱,但还有很多替代品,例如 Google Guice .

使用 Spring,您可以定义两个(三个、十五个……)独立的上下文,每种语言一个,并从适当的上下文中获取所需的组件。它类似于您的第二种方法,但不使用 Language 类。例如:

# 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");

另一种选择是有一个单一的上下文,但在你的 bean id 前面加上你的语言,例如“English-Tokenizer”和“Chinese-Tokenizer”。

如果您以前从未使用过依赖注入(inject),那么对于可以通过工厂和/或动态类加载实现的结果来说,这听起来似乎工作量太大了:-)但事实并非如此-而且它可以做更多的事情(您可以配置组件的属性/依赖项;您不必担心缓存或维护您自己的单例等...),一旦您开始使用它,您就会想知道没有它您是如何生活的:- )

更新(回答第二条评论中的问题)。

这是一个示例“ComponentLocator”模式。 ComponentLocator 是一个不依赖于 Spring 的单例。它的实例(和实现)由上下文注入(inject)。

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);
  }
}

ComponentLocator 的实现假定您上下文中的 bean 被命名为它们的接口(interface)名称,后跟分号和语言(例如“com.mypackage.Parser:English”)。 ComponentLocatorImpl 必须在您的上下文中声明为 bean(bean 名称无关紧要)。

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));
  }
}

在您其他地方的代码中(在 main() 中?)您将加载 ApplicationContext:

ApplicationContext ctx = new ClasspathXmlApplicationContext("components.xml");

请注意,您实际上不需要在应用程序的其他任何地方直接引用上下文。无论您需要在哪里获取组件,您都可以:

Parser englishParser = ComponentLocator.getComponent(Parser.class, "English");
Parser chineseParser = ComponentLocator.getComponent(Parser.class, "Chinese");

请注意,以上只是一种可能的方法,它假设您几乎只将依赖于语言的类放在上下文中。在您的情况下,这可能是最好的(由于要求所有语言同时可用),否则您将复制所有类(class)(每种语言一次),因此您的 A/B/C 问题可能不适用于此处。

但如果你确实有 A/B/C 依赖,你可以做的是(我假设 A、B、C 是接口(interface),Aimpl、Bimpl、Cimpl 是它们的实现):

<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>

您的实现需要有 setB()、setC() 和 setTokenizer() 方法。这比构造函数注入(inject)更容易,尽管后者也是可能的。

关于java - 多语言组件/类 [OOP/Patterns/IoC/DI],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1217143/

相关文章:

c# - 依赖注入(inject)和接口(interface)的许多实现

c# - 构造函数 + 依赖注入(inject)

Java hibernate获取所有实体

java - 使用 Preon 框架进行简单算术

wpf - WPF中的应用程序屏幕管理

java - 如何在 PageObjects 模式中使用 WebDriver/Selenium 2 LoadComponents?

java.lang.reflect.InitationTargetException

java - BufferedReader 构造函数期望 FileReader 是什么

c# - 如何确定我在列表中引用的子类?

c++ - 哪种设计模式适合这种情况?