基于状态的实体处理的设计模式

Design pattern for state-based entity handling

我的问题是什么应该是最适合我的情况的 OOP 解决方案和正确的设计模式。我们有一个用户实体,多个帐户实体属于该用户。帐户实体可以有多个状态,我们可以对帐户执行多个操作。这些操作的结果基于帐户实体的状态。

我有以下主要基于开关的代码(有时它看起来像一些“如果”)。想改但是找不到合适的设计模式

enum Status {
    ACTIVE, INACTIVE, DELETED; 
}

@Entity
class Account {
    private long id;
    private long userid;
    private Status status;

    //...
}

class AccountService{

    Account delete(long id) {
        //...
        
        if (accountInfo.getSatus() == DELETED) {
            throw new IllegalOperationException();
        }
        if (accountInfo.getStatus() == ACTIVE || accountInfo.getStatus()) {
            accountInfo.setStatus(DELETED);
            accountInfoRepository.save(accountInfo);
        }
    }

    Account create (Account account) {
        // various operations based on state
    }
}

我真的很想重构这些代码,我担心一旦我们的服务增长,它就会包含更多的“魔力”并且难以维护。如果我们想引入一个新的状态,这几乎是不可能的。

我初级的想法认为我应该有状态对象来实现所有的操作,伪代码风格:

class AccountService {
    private StateFactory stateFactory;
    private AccountRepository accountRepository;

    Account delete(long id) {
        final Optional<Account> account = accountRepository.findById(id);
        Account deletedAccount = account.map(stateFactory::getByState)
                                        .map(accountState -> accountState.delete(account))
                                        .orElseThrow(() -> new IllegalOperationException());
        return accountRepository.save(deletedAccount); 
    }

    Account create (Account account) {
        // various operation based on state
    }
}

和:

class ActiveState extends AccountState {

    @Override
    public Account delete(Account account) {
        //implementation
    }

    @Override
    public Account activate(AccountInfo) {
        // implementation
    }
}

和:

interface AccountState {
    Account activate(AccountInfo);
    Account delete(AccountInfo);
}

我知道这个问题一定有更好的实现。还有哪些其他设计模式适合此设置?

更新

我在主题中发现了几篇有趣的文章:

How to implement a FSM - Finite State Machine in Java

When you have more complex state handling

如果我对问题的理解正确,那么有必要根据其状态应用一些操作。 如果为真,那么我们可以使用工厂模式来获取可以执行某些操作的所需对象。 state和action的映射可以放到HashTable.

让我们看一个代码示例。我将通过 C# 编写,但这段代码可以很容易地转换为 Java,因为语言有很多语法相似之处。

所以我们将有状态枚举:

public enum Status 
{ 
    Active,
    Deleted,
    Foo
}

AccountState

的状态
public abstract class AccountState
{
    public abstract void ExecSomeLogic();
}

public class ActiveState : AccountState // "extends" instead of ":" in Java
{
    public override void ExecSomeLogic()
    {
    }
}

public class DeletedState : AccountState // "extends" instead of ":" in Java
{
    public override void ExecSomeLogic()
    {
    }
}

public class FooState : AccountState // "extends" instead of ":" in Java
{
    public override void ExecSomeLogic()
    {
    }
}

然后我们需要 Status 的映射器 class 到他们的 AccountState:

public class StatusToAccountState 
{
    public Dictionary<Status, AccountState> AccountStateByStatus { get; set; } = 
        new Dictionary<Status, AccountState>() // HashMap in Java
    {
        { Status.Active, new ActiveState() },
        { Status.Deleted, new DeletedState() },
        { Status.Foo, new FooState() },
    };
}

然后在您的服务中您可以这样使用它:

void Delete(long id, Status status)
{
    StatusToAccountState statusToAccountState = new StatusToAccountState();
    AccountState accountState = statusToAccountState.AccountStateByStatus[status];
    accountState.ExecSomeLogic();
}

如果有很多逻辑来弄清楚对象的 Status 是什么,那么您可以创建一些 class,它只负责弄清楚对象的状态:

public class StatusManager
{
    public Status Get() 
    {
        return Status.Active; // or something another based on logic
    }
}

这样做之后,你的classes将对应SOLID的单一职责原则。 Read more about single responsibility principle of SOLID here

太多 switch-/if-Statements 表明代码有“工具滥用者”的味道(请参阅 M. Fowler“重构”)。使用多态机制来解决这个问题。 https://refactoring.guru/smells/switch-statements