Guice - Как предоставить API, поддерживающий гибкие реализации подключаемых стратегий со своими привязками? - PullRequest
1 голос
/ 27 января 2012

Мы разрабатываем API, который позволяет клиентам предоставлять ряд подключаемых стратегий (конкретных реализаций интерфейса стратегии), которые составляют отдельный экземпляр приложения.Кроме того, мы бы хотели, чтобы они были определены в файле конфигурации экземпляра, чтобы их можно было изменять без необходимости новой сборки.

Чтобы сделать наш API легко тестируемым и поддерживаемым, мы продвигаемся вперед.используем Guice в качестве нашей DI-фреймворка.Следовательно, вышеупомянутые стратегии будут связаны через расширение Multibinder, поскольку для данного экземпляра приложения может быть любое количество реализаций.С каждой стратегией связан идентификатор, поэтому она добавляется в картографическое средство, которое отображает идентификатор стратегии -> класс реализации стратегии.

Проблема, с которой мы сталкиваемся при таком подходе, заключается в том, что эти стратегии сами могут использовать шаблон стратегии.Поэтому вы могли бы использовать эти стратегии как экземпляры одного и того же класса, но с разными реализациями зависимостей.Стратегии можно оставить для настройки их собственных привязок, но это приведет к тому, что они попытаются связать разные реализации одного и того же интерфейса (я думаю, что это похоже на проблему с ногами робота?)

Чтобы взять простой пример,скажем, наш API зависел от HelloWorld стратегий, определенных в конфигурации, и это были реализации следующего интерфейса.

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.Поэтому подход, который мы рассмотрели, состоит в том, чтобы позволить самим экземплярам модуля указываться в конфигурации.Однако при таком подходе вы получите коллизии, когда два разных модуля попытаются связать разные реализации с интерфейсом CountryStrategy.

Подход, который мы собираемся применить, заключается в том, чтобы предоставить разработчикам клиента гарантию того, что модулиони пишут, что связывают реализации стратегии изолированно.Мы делаем это, имея модуль установки, который создает отдельный инжектор для каждого модуля, который связывает одну из этих реализаций 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);
    }
}

Это работает, но кажетсянемного излишним для того, чего мы пытаемся достичь, который фактически пытается внедрить шаблон стратегии на основе конфигурации.Кто-нибудь сталкивался с подобной проблемой или есть лучшие способы решения этих проблем?

Буду признателен за любые мысли.

Большое спасибо за чтение, если вы зашли так далеко.

1 Ответ

0 голосов
/ 27 января 2012

Я решил ту же проблему, передав экземпляр 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
        ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...