【发布时间】:2016-12-29 22:17:43
【问题描述】:
我正在尝试使用 Java 中的面向对象技术为状态机创建一个框架。每个System 一次只能有一个State 处于活动状态。
public interface State {}
public interface System {
State currentState();
}
假设我想添加一种应定期更新以检查和更改其状态的新型系统,称为UpdateableSystem。它只能具有 UpdateableState 类型的状态。
public interface UpdateableState extends State {
/**
* Check to see if the system should switch to a different state.
* @returns new state of the system
*/
UpdateableState update();
}
public interface UpdateableSystem extends System {
@Override
UpdateableState currentState();
/**
* Runs update method of the current state, or otherwise updates the system.
* @returns the current state of the system after the update
*/
UpdateableState update();
}
我重写了currentState 方法,只返回UpdateableState,而不是State,因为UpdateableSystem 只能具有UpdateableState 类型的状态,而使用UpdateableSystem 的客户端将期待UpdateableStates。
为了避免在创建更多子类时多次覆盖许多方法,泛型似乎是解决方案。 这是更新的界面。
public interface System<SystemState extends State> {
SystemState currentState();
}
和
public interface UpdateableState<SystemState extends UpdateableState<SystemState>> extends State {
SystemState update();
}
public interface UpdateableSystem<SystemState extends UpdateableState<SystemState>> extends System<SystemState> {
SystemState update();
}
现在,假设我想添加另一种类型,接口Context,系统和状态需要注意。 Context 应该能够被子类型化,并且客户端和ContextState 子类型应该能够使用该Context 子类型的完整接口。
public interface Context {}
public interface ContextState<SystemContext extends Context, SystemState extends ContextState<SystemContext, SystemState>> extends UpdateableState<SystemState> {
SystemState updateWithContext(SystemContext context);
}
public interface ContextSystem<SystemContext extends Context, SystemState extends ContextState<SystemContext, SystemState>> extends UpdateableSystem<SystemState> {
// Some methods here that require SystemContext type
}
突然之间,这些参数化类型变成了一大堆样板文件,令人困惑且难以维护。
这将是一个具有具体实现的示例:
class CarContext implements Context {...}
interface CarState extends ContextState<CarContext, CarState> {...}
class CarDrivingState implements CarState {}
class CarIdleState implements CarState {}
class Car implements ContextSystem<CarContext, CarState> {...}
每个具体的CarState 将能够访问CarContext 接口提供的其他方法,因为从ContextState 继承的方法签名将由CarContext 类型参数化。因此,updateWithContext 的上下文参数不需要显式转换。
最终,问题在于添加额外类型以进行参数化时的样板过多,而且我不知道有一种设计替代方案可以通过继承实现代码重用,同时保持强大的类型系统,尽可能避免显式转换。有什么改进这个设计的建议吗?
【问题讨论】:
-
Java 泛型不是模板类型。这个
interface CarState extends ContextState<CarContext, CarState>(例如)也可以写成interface CarState extends ContextState<A, B>——该类型的本地名称只是一个标签。 -
我不确定您的意思,它们在
ContextState接口的定义中充当标签,但在CarState接口的定义中,它们必须是实际类型。据我所知,实际的接口或类。 -
我会再试一次; Java 泛型是一个编译时类型检查系统。编译后它们是erased(因此没有运行时开销)成
java.lang.Object;您似乎期望的是它们的行为类似于 C++ 模板类型。他们没有。 -
@Elliott 抱歉,如果我误解了,但我可以将其扩展为原始类型:
interface CarState extends ContextState,并且它可以功能相同,因为擦除。但是,我会收到编译器警告,并且在编译时它会缺乏相同的类型检查保证。我不确定在这种情况下擦除对我有什么帮助。 -
擦除对您没有帮助。擦除是 Java 中如何实现泛型的直接(也是不可避免的)结果。
标签: java generics inheritance design-patterns