在我的DDD项目中,我试图用java enum实现状态模式。
在验证具有依赖于状态的行为的实体方法时,我遇到了问题。
为了验证,我使用通知模式。
我遵循“始终有效的实体”方法,以便在每个操作中首先调用"isValidForOperation“验证方法。
下面是代码,与简单性相关:
实体:
public class Task extends AggregateRoot<TaskId> {
...
private State state;
...
// Operation with behaviour depending on the state
// It's a transition from "ASSIGNED" state to "IN_PROGRESS" state
// I apply the state pattern here
public void start () {
State next = this.state.start ( this );
this.setState ( next );
}
...
}java enum对状态进行建模:
public enum State {
ASSIGNED {
public State start ( Task task ) {
// Validation method to ensure the operation can be done
assertTaskIsValidForStart ( task );
// Business logic
...
// Return the next state
return ( State.IN_PROGRESS );
}
}
...
// more enum values for other states
...
// Default implementation of "start" operation
// It will be executed when the current state is not "ASSIGNED"
// So an error would be generated
public State start ( Task task ) {
// I can't apply notification pattern here !!!
// I would have to throw an exception
}
}验证方法遵循通知模式。它收集通知对象中所有可能的错误。此通知对象传递给异常。抛出异常,然后应用程序层捕获异常并将所有错误消息返回给客户端。
public void assertTaskIsValidForStart ( Task task ) {
Notification notification = new Notification();
if ( errorCondition (task) ) {
notification.addError(...);
}
...
// more errors
...
if ( notification.hasErrors() ) {
throw new TaskNotValidForStartException ( notification.errors() );
}
}当错误条件是关于状态之间无效的转换时,如何应用通知模式(与状态模式合并)?
有什么想法吗?
更新:
我找到了解决办法。我将依赖于实体中状态的整个操作放到实体中,并将状态模式应用到更细粒度的代码中,仅用于所需的代码。这样,我就可以应用模式来计算下一个状态,这样我就可以检查是否允许转换,也可以应用通知模式。
代码:
public class Task extends AggregateRoot<TaskId> {
...
private State state;
...
// Operation with behaviour depending on the state
// It's a transition from "ASSIGNED" state to "IN_PROGRESS" state
// I apply fine-grained state pattern here
public void start () {
// Validation method to ensure the operation can be done
// One of the validations will be if the transition is allowed
assertTaskIsValidForStart ( this );
// Business logic
// If it depends on the state, I would apply state pattern delegating to another method
...
// Set the next state
State next = this.nextStateForStart();
this.setState ( next );
}
...
public State currentState() {
return this.state;
}
...
public State nextStateForStart() {
return this.currentState().nextStateForStart();
}
...
}
public enum State {
ASSIGNED {
public State nextStateForstart() {
return ( State.IN_PROGRESS );
}
}
...
// more enum values for other states
...
// Default implementation of "start" transition
// It will be executed when the current state is not "ASSIGNED"
public State nextStateForstart() {
return null;
}
}
public void assertTaskIsValidForStart ( Task task ) {
Notification notification = new Notification();
// Validate the transition is allowed
if ( task.nextStateForStart() == null ) {
notification.addError(...);
}
...
// more errors
...
if ( notification.hasErrors() ) {
throw new TaskNotValidForStartException ( notification.errors() );
}
}发布于 2017-10-12 05:28:37
我觉得你的枚举做得太多了。除了有一组很难扩展的固定状态之外,您还很难为每个具体状态引入任何形式的合同,这也将解决您的通知问题。
引入一个抽象状态类,它是所有具体状态的基类。传递一个上下文,该上下文允许为每个州设置一个后续状态。这个上下文可以由聚合根实现。您的通知可以通过使用AbstracftState强制执行的方式由每个状态管理,例如强制状态执行返回通知对象:
interface StateContext {
setState(AbstractState state);
}
class AbstractState {
abstract Notification execute(StateContext context);
}
class Task extends AggregateRoot implements StateContext {
AbstractState currentState;
....
public void start() {
Notification n = currentState.execute(this);
if (n.hasErrors()) {
throw new Exception(n.toErrorReport());
}
}
}现在,您可以在执行之前或之后收集每个状态的错误(您可能希望在每个validateStart()中引入一个在执行前调用的AbstractState ()),并向调用方报告收集的错误。
发布于 2017-10-13 02:03:55
我会在任务聚合中将TaskWorkflow建模为VO。
class Task {
private Workflow workflow;
public void start() {
workflow = workflow.followWith(Action.START, this);
}
public State currentState() {
return workflow.state();
}
public List availableActions() {
return workflow.nextActions();
}
}工作流是由由动作连接的状态之间的转换组成的FSM。任何对工作流方法的调用都会创建指向新状态的新工作流表示。如您所说,可以将转换建模为直接的或更复杂的涉及业务逻辑的自定义。如果使用函数式语言,您可以返回Monad来处理错误,但在本例中,您可以还原并创建错误,或者只需抛出表示聚合消息的异常。
希望能帮上忙。
https://stackoverflow.com/questions/46700832
复制相似问题