业务逻辑验证模式和建议

2022-09-02 09:48:28

我的应用程序中有两层验证。首先是通过 Bean 验证 API 执行的实体验证(例如必填字段)。第二个级别是业务逻辑验证。例如,用户有一个帖子。只有当用户是此帖子的创建者并且帖子评级< 50 时,他才能删除帖子。所以我必须做这样的事情:

if (post.getCreator().equals(session.getUser())) {
  if (post.getRating() < 50) {
    postRepository.delete(post);
  } else errors.add(400, "Cant delete post with rating 50 or higher")
} else errors add (400, "You should be owner of the post")

我不喜欢这种方式,因为这个条件是重用的,我必须复制代码。此外,如果条件数大于 5 左右,则读取和理解代码变得不真实。

此外,标准的Spring Validator不会很有帮助,因为我必须在不同的操作(例如删除和更新)上为一个实体进行不同的验证。

所以我正在寻找一种方法来以更聪明的方式做到这一点(也许是模式),如果有人能给我一个提示,我将不胜感激。

提前致谢!


答案 1

您可以使用策略模式

每个条件都可以建模为一个函数,该函数采用 post 和会话上下文,并可能返回错误:

Post -> Session -> Optional<String> 

你可以用一个接口来表示这一点:

@FunctionalInterface
public interface ValidationCondition {

    Optional<String> validate(final Post post, final Session session);
}

例如:

public class CreatorValidation implements ValidationCondition {
    
    public Optional<String> validate(final Post post, final Session session) {
        if (post.getCreator().equals(session.getUser()) {
            return Optional.empty();
        }
        return Optional.of("You should be the owner of the post");
    }
}

然后,您可以将每个验证存储在列表中:

final List<ValidationCondition> conditions = new ArrayList<>();

conditions.add(new CreatorValidation());
conditions.add(new ScoreValidation());
// etc.

使用该列表,可以批量应用验证:

final List<String> errors = new ArrayList<>();

for (final ValidationCondition condition : conditions) {
    final Optional<String> error = condition.validate(post, session);
    if (error.isPresent()) {
        errors.add(error.get());
    }
}

使用 Java 8 lambdas,您可以内联声明这些:

final ValidationCondition condition = (post, session) -> {
    // Custom logic
});

答案 2

在我看来,策略模式是解决方案。我给大家举一个非常简单的例子。假设我们有两种信用卡,维萨卡和万事达卡。两张卡执行支付操作的逻辑相同,但卡号验证不同。因此,通过工作流传递VisaStrategy对象,执行与我们传递万事达卡Strategy相同的逻辑和操作,除了一件事 - 卡号验证,这是在每个定义的Strategy类中完成的,因此您的代码中根本没有任何“如果其他”的东西。现在,每个策略类负责一种且仅一种类型的卡验证。如果你寻找灵活且易于维护的代码结构 - 使用策略设计模式。


推荐