基于枚举的状态机,Dagger2注入状态处理程序

时间:2018-02-06 03:05:46

标签: java enums dagger-2

我试图创建一个使用枚举来指定状态的状态机。由于存在很多状态并且每个状态实现的逻辑非常复杂,我想将每个状态与将在不同类中定义的状态处理程序相关联。每个状态处理程序都会实现一个公共接口(或扩展一个公共抽象类),但每个状态处理程序可能都有自己的一组注入依赖项,其他人可能不需要。到目前为止,这样的事情......

StateHandler接口:

public interface StateHandler {
    void onActivation();
    void onDeactivation();
}

StateHandlers示例:

@Singleton
public class DefaultStateHandler implements StateHandler {
    @Inject
    public DefaultStateHandler(SomeDependency someDependency) {...}
    /** implement onActivation, onDeactivation and state specific logic **/
}

@Singleton
public class OtherStateHandler implements StateHandler {
    @Inject
    public OtherStateHandler(SomeOtherDependency someOtherDependency) {...}
    /** implement onActivation, onDeactivation and state specific logic **/
}

StateManager实施:

@Singleton
public class StateManager {
    private StateType stateType = StateType.DEFAULT;

    @Inject
    public StateManager() { }

    public void changeState(StateType newStateType) {
        if (stateType != newStateType) {
            stateType.getStateHandler().onDeactivation();
            stateType = newStateType;
            stateType.getStateHandler().onActivation();
        }
    }
}

枚举定义:

public enum StateType {
    DEFAULT (/* not sure what to do here */),
    OTHER_STATE (...);

    private StateHandler stateHandlerInstance;
    public getStateHandler { return stateHandlerInstance; }

    StateType(/* not sure what to do here */) {
        /* assign stateHandlerInstance */
    }
}

我想弄清楚的是......在声明相关枚举时,如何注入状态处理程序的特定实例?或者,如果这是不可能的,是否有另一种方法为每个枚举指定状态处理程序类,然后(在构造函数中或在第一次需要时),获取关联的状态处理程序实例?

1 个答案:

答案 0 :(得分:0)

我原本以为我需要将状态处理程序实例注入状态枚举定义。但是,由于注入需要公共构造函数和枚举使用私有构造函数,我不认为这种方法是可行的。

正如上面的评论所述,解决方案是使用地图多重绑定。

首先简化枚举StateType:

public enum StateType {
    DEFAULT, OTHER_STATE
}

现在我们需要一个特定于此枚举类型的匕首MapKey接口:

@MapKey
@interface StateTypeKey {
    StateType value();
}

接下来我们需要一个dagger模块,它将为每个StateType / StateHandler组合提供提供者函数:

@Module
public StateTypeHandlersModule {

    // @Provides @IntoMap               // Syntax for dagger >= 2.9
    @Provides(type = Provides.Type.MAP) // Syntax for dagger <= 2.8
    @StateTypeKey(StateType.DEFAULT)
    StateHandler provideDefaultStateHandler(DefaultStateHandler handler) {
        return handler;
    }

    // @Provides @IntoMap               // Syntax for dagger >= 2.9
    @Provides(type = Provides.Type.MAP) // Syntax for dagger <= 2.8
    @StateTypeKey(StateType.OTHER_STATE)
    StateHandler provideOtherStateHandler(OtherStateHandler handler) {
        return handler;
    }

}

遗憾的是很多样板代码,这就是为什么我只为处理程序使用一个单独的模块,并将其包含在更高级别的状态机模块中。请注意,如果使用相同的StateTypeKey声明两个提供程序函数,则第二个处理程序最终在注入的映射中可用。

最后,我们可以将Map<StateType, StateHandler>注入StateManager:

@Singleton
public class StateManager {
    private Map<StateType, StateHandler> stateHandlerMap;
    private StateType stateType = StateType.DEFAULT;

    @Inject
    public StateManager(Map<StateType, StateHandler> stateHandlerMap) {
        this.stateHandlerMap = stateHandlerMap;
    }

    public void changeState(StateType newStateType) {
        if (stateType != newStateType) {
            stateHandlerMap.get(stateType).onDeactivation();
            stateType = newStateType;
            stateHandlerMap.get(stateType).onActivation();
        }
    }
}