状态模式和域驱动设计

时间:2012-04-04 13:13:57

标签: domain-driven-design state

我们经常使用简单的枚举来表示我们实体上的状态。当我们引入主要取决于状态的行为,或者状态转换必须遵守某些业务规则时,就会出现问题。

采用以下示例(使用枚举来表示状态):

public class Vacancy {

    private VacancyState currentState;

    public void Approve() {
        if (CanBeApproved()) {
            currentState.Approve();
        }
    }

    public bool CanBeApproved() {
        return currentState == VacancyState.Unapproved
            || currentState == VacancyState.Removed
    }

    private enum VacancyState {
        Unapproved,
        Approved,
        Rejected,
        Completed,
        Removed
    }
}

你可以看到,当我们为Reject,Complete,Remove等添加方法时,这个类很快会变得非常冗长。

相反,我们可以引入State模式,它允许我们将每个状态封装为一个对象:

public abstract class VacancyState {

    protected Vacancy vacancy;

    public VacancyState(Vacancy vacancy) {
        this.vacancy = vacancy;
    }

    public abstract void Approve(); 
    // public abstract void Unapprove();
    // public abstract void Reject();
    // etc.

    public virtual bool CanApprove() {
        return false;
    }
}

public abstract class UnapprovedState : VacancyState {

    public UnapprovedState(vacancy) : base(vacancy) { }

    public override void Approve() {
        vacancy.State = new ApprovedState(vacancy);
    }

    public override bool CanApprove() {
        return true;
    }
}

这使得在状态之间转换变得容易,根据当前状态执行逻辑或在需要时添加新状态:

// transition state
vacancy.State.Approve();

// conditional
model.ShowRejectButton = vacancy.State.CanReject();

这种封装看起来更清晰,但是如果有足够的状态,这些也会变得非常冗长。我读了Greg Young's post on State Pattern Misuse,建议使用多态(因此我会有ApprovedVacancy,UnapprovedVacancy等类),但是看不出这对我有什么帮助。

我应该将这种状态转换委托给域服务,还是我在这种情况下使用状态模式是正确的?

1 个答案:

答案 0 :(得分:5)

要回答您的问题,您不应将其委托给域名服务,并且您对状态模式的使用几乎是正确的。

详细说明,维护对象状态的责任属于该对象,因此将其降级为域服务会导致贫血模型。这并不是说国家修改的责任不能通过使用其他模式来授权,但这应该对对象的消费者是透明的。

这导致我使用State模式。在大多数情况下,您正确使用该模式。你偏离了一点的部分是你的德米特法则违规。您的对象的使用者不应该进入您的对象并调用其状态的方法(例如vacancy.State.CanReject()),而是您的对象应该将此调用委托给State对象(例如vacancy.CanReject() - > bool CanReject() { return _state.CanReject(); })。对象的使用者不必知道您甚至使用State模式。

要对您引用的文章发表评论,状态模式依赖于多态,因为它是促进机制。封装State实现的对象能够将调用委托给当前分配的任何实现,无论是什么都不做,抛出异常或执行某些操作。此外,虽然通过使用状态模式(或任何其他模式)肯定可能导致Liskov替换原则违规,但这不是由对象可能抛出异常的事实决定的,而是由对对象的修改可以根据现有代码制作(阅读this以进一步讨论)。