java - Guice - 如何提供一个 API,支持灵活的可插拔策略实现及其自己的绑定(bind)?

标签 java dependency-injection guice strategy-pattern

我们正在设计一个 API,允许客户提供许多可插入策略(策略接口(interface)的特定实现)来构成应用程序的单个实例。此外,我们最好希望在实例的配置文件中定义它们,以便无需重新构建即可更改它们。

为了使我们的 API 易于测试和维护,我们使用 Guice 作为我们的 DI 框架。因此,上述策略将通过 Multibinder 扩展进行绑定(bind),因为应用程序的给定实例可以有任意数量的实现。每个策略都有一个与之关联的标识符,因此它被添加到映射策略标识符 -> 策略实现类的 MapBinder 中。

我们在使用这种方法时遇到的问题是这些策略本身可能会利用策略模式。因此,您可以将这些策略作为同一类的实例,但注入(inject)不同的依赖实现。这些策略可以保留配置自己的绑定(bind),但这将导致它们尝试绑定(bind)同一接口(interface)的不同实现(我认为这类似于机器人腿问题?)

举一个简单的例子,假设我们的 API 依赖于配置中定义的 HelloWorld 策略,并且这些是以下接口(interface)的实现。

public interface HelloWorldStrategy {
    public String getMessage(String caller);
}

在我们的“可插入”代码中,我们有以下实现。

public class HelloWorld implements HelloWorldStrategy {
    public String getMessage(String caller) { return "Hello World"; }
}

public class HelloCountry implements HelloWorldStrategy {
    private final CountryStrategy countryStrategy;

    @Inject
    public HelloCountry(CountryStrategy countryStrategy) {
        this.countryStrategy = countryStrategy;
    }

    public String getMessage(String caller) { 
        return "Hello " + countryStrategy.getCountry(String caller);
    }
}

在我们的配置文件中,我们可能想要定义 HelloCountry 类的多个实例,但它们对 CountryStrategy 接口(interface)具有不同的绑定(bind)。因此,我们考虑的方法是允许在配置中指定模块实例本身。但是,在这种方法中,当两个不同的模块尝试将不同的实现绑定(bind)到 CountryStrategy 接口(interface)时,您会发生冲突。

我们正在考虑采取的方法是向客户端开发人员提供保证,确保他们编写的绑定(bind)策略实现的模块是隔离的。我们通过一个设置模块来实现这一点,该模块为每个模块创建一个单独的注入(inject)器,该注入(inject)器绑定(bind) HelloWorldStrategy 的这些实现之一。对于上面的示例,这看起来如下所示。

public class HelloWorldStrategySetupModule extends AbstractModule {

private final List<HelloWorldStrategyModule> strategyModules;
private final Injector parentInjector;

public HelloWorldStrategySetupModule(List<HelloWorldStrategyModule> strategyModules, Injector parentInjector) {
    this.strategyModules = strategyModules;
    this.parentInjector = parentInjector;
}

@Override
protected void configure() {
    MapBinder<String, HelloWorldStrategy> mapbinder = MapBinder.newMapBinder(binder(), String.class, HelloWorldStrategy.class);

    for(HelloWorldStrategyModule strategyModule : strategyModules) {
        Injector strategyModuleInjector = parentInjector.createChildInjector(strategyModule);
        mapbinder.addBinding(strategyModule.getIdentifier()).toInstance(strategyModuleInjector.getInstance(HelloWorldStrategy.class));
    }
}

策略模块的示例(在配置中定义)如下:

public class EuropeanCountryStrategyModule extends AbstractModule {
    @Override
    protected void configure() {
        binder().bind(CountryStrategy.class).to(EuropeanCountriesStrategy.class);
        binder().bind(HelloWorldStrategy.class).to(HelloCountry.class);
    }
}

这可行,但对于我们想要实现的目标来说似乎有点矫枉过正,即有效地尝试合并基于配置的策略模式。有没有人遇到过类似的问题或有解决这些问题的最佳实践方法?

如有任何想法,我们将不胜感激。

如果您读到这里,非常感谢您的阅读。

最佳答案

我通过将 org.apache.commons.configuration.Configuration 实例传递给我的模块解决了同样的问题。我在应用程序的主体中加载此配置。

public class FlywheelModule extends AbstractModule
{

private Configuration topologyConfig;

public FlywheelModule(Configuration topologyConfig)
{
    this.topologyConfig = topologyConfig;
}
...

然后从配置方法中解析:

if (topologyConfig.getString("topology.instanceConfiguration.persistence[@type]").equalsIgnoreCase("XML"))
    {
        bind(InstanceConfigurationPersistenceStrategy.class).to(XmlInstanceConfigurationPersistenceStrategy.class).in(Singleton.class);
        bind(InstanceConfiguration.class).toProvider(InstanceConfigurationProvider.class).in(Singleton.class);
    }
    else
        ...

关于java - Guice - 如何提供一个 API,支持灵活的可插拔策略实现及其自己的绑定(bind)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9033159/

相关文章:

java - 获取资源的大小

java - 如何使用图像库在 Java 中创建自定义 JButton?

java - 使用 Dropwizard 和 JDBI 查询具有多个模式的数据库?

Angular 2 - 无法解析组件 : (? 的所有参数)

java - Guice 字段注入(inject)不起作用(返回 null)

java - Guice 将对象注入(inject)到类构造函数中

java - 在 Web 应用程序中获取 Guice Injector

java - 相机 2 基本 : Application:transformClassesWithDexForDebug

php - 服务定位器、依赖注入(inject)(和容器)和控制反转

java - 部署时使用 Guice InstantiationError 进行 RESTEasy(App Engine 本地)