7

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

举个例子(使用枚举来表示状态):

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 等方法。

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

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 关于状态模式滥用的帖子,它建议改用多态性(所以我会有 ApprovedVacancy、UnapprovedVacancy 等类),但看不出这对我有什么帮助。

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

4

1 回答 1

6

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

详细地说,维护对象状态的责任属于该对象,因此将其委托给域服务会导致模型贫乏。这并不是说不能通过使用其他模式来委派状态修改的责任,但这应该对对象的使用者是透明的。

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

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

于 2012-04-04T14:44:58.093 回答