Java 验证具有不同策略的值对象-密码大小写

Java 验证具有不同策略的值对象-密码大小写,java,validation,domain-driven-design,value-objects,ddd,Java,Validation,Domain Driven Design,Value Objects,Ddd,我正在设计的图书馆,这是应该帮助我在未来的项目设计领域。我制作了一些基本的值对象,比如EmailAddress或PhoneNumber。这些很简单。当这样的值对象可能有不同的规则集来判断它是否有效时,我开始觉得有问题了 让我们用Password作为例子,因为这是我写这个问题的真正原因。在以前的项目中,我在passwordconstrutor中验证了密码规则集。但它使这个实施项目具体化,并且违反了——我认为——关注分离原则 我认为战略模式是这里的解决方案。但是这样构造VO:新密码(“123456”

我正在设计的图书馆,这是应该帮助我在未来的项目设计领域。我制作了一些基本的值对象,比如EmailAddress或PhoneNumber。这些很简单。当这样的值对象可能有不同的规则集来判断它是否有效时,我开始觉得有问题了

让我们用
Password
作为例子,因为这是我写这个问题的真正原因。在以前的项目中,我在
password
construtor中验证了密码规则集。但它使这个实施项目具体化,并且违反了——我认为——关注分离原则

我认为战略模式是这里的解决方案。但是这样构造VO:
新密码(“123456”,新的AwfullyLoosePasswordValidationStrategy())
听起来很糟糕。我还想到了VO
setValidationStrategy(PasswordValidationStrategy策略)
中的静态setter,但这仍然不利于关注点分离,对我来说听起来很奇怪

所以第一部分的回答是——我认为——密码应该只做基本的健全性检查,比如
isNullOrEmptyString
等等。但我什么时候应该做适当的验证?在实体创建期间?在服务层的某个地方持久化之前?实体的想法听起来对我来说并不坏,但是
setPassword(Password Password
)如何知道我想要使用的策略呢?我在项目中尽量避免单身,几乎所有的事情都是通过DI完成的

tl;dr:我应该在哪里验证无法在其构造函数中验证的值对象?

您可以使用来创建复杂的对象

公共类密码{ 私有字符串密码

private Password(String password){

}


public static class Builder{
    private String password;
    private Validation validation;

    public Builder(){
    }

    public Builder setPassword(String password){
        this.password = password;
    }

    public Builder setValidation(Validation validation){
        this.validation = validation;
    }

    public Password build(){
        if (null == validation){
             return null;
        }
        if (validation.validate(password)){
             return new Password(password);
        }
        else{
             return null;
        }
    }
}
}
然后,您可以将其用作

Password password = new Password.Builder().setPassword("123456")
                                          .setValidation( new AwfullyLoosePasswordValidationStrategy())
                                          .build();
你的问题听起来不像是在尝试DDD方式,但是你用DDD标签标记了你的问题-所以我假设你想要一个DDD答案:-)

首先,不要将复杂的验证放在VOs中。这会使它们难以使用。VOs本质上是一个简单的概念,因此请尽量保持这种方式。尤其是向服务添加引用(如您建议的策略)通常是一个坏主意

将值对象验证放在何处 因此,您应该只验证VO中最基本的东西,比如null检查。regex检查已经太多IMHO了,但我相信对此有不同的看法

既然VOs中没有验证逻辑,您需要准备好查看无效的VO。域模型应该在VO进入域后立即验证VO,例如,在以VO为参数的实体方法中。

使验证逻辑可重用 如果将VO验证逻辑放在实体方法中,很快就会出现这样的情况:您希望验证来自另一个实体方法的同一VO。处理此问题的最佳方法是规范模式。

提供模式的描述。规范的优点在于它们是可重用和可组合的,例如,用几个较小的规范来制作更大的规范。此外,它们往往会记录特定VO的验证工作情况,并且它们可以依赖于服务

对于不同密码策略的特定情况,您可以只实现一系列不同的规范,每个策略一个规范。您可以将更简单的规范组合成更复杂的规范

我应该在哪里验证无法在其构造函数中验证的值对象

我不相信你有这样的东西。你永远不应该创建无效的值对象。构造函数(如果你愿意,替换工厂或工厂方法)验证参数,如果它们是可接受的,则创建一个格式良好、不可变的值对象,那么你就完成了

我本来想用策略模式作为解决方案,但这样构造VO:新密码(“123456”,new awfullylosePasswordValidationStrategy())对我来说听起来很糟糕

我不知道在任何情况下,策略模式在值对象中都是有意义的。如果值类型的查询需要策略,则更有可能将策略作为参数传递给查询,而不是将其作为类型的固有参数

也就是说,在我看来,你已经非常接近正确的想法了;你只是在倒退

PasswordValidator validator = new AwfullyLoosePasswordValidator();
Password password = validator.createPassword("123456");
也就是说,工厂根据您的策略验证输入,如果可以接受,则将该输入传递给密码构造函数(密码构造函数也执行自己的检查)

或者,您可以将密码策略作为实体创建的一部分而不是密码创建的一部分来实现

class EntityFactory {
    private final PassswordValidator passwordValidator;

    Entity create(Password password) {
        passwordValidator.check(password);
        return new Entity(password);
    }
实体的想法听起来对我来说并不坏,但setPassword(Password Password)如何知道我想要使用的策略呢

这是一个非常重要的问题,你应该仔细研究

因为如果仔细查看需求,您可能会发现setPassword()方法并不存在于明显的位置

如果将密码建模为实体的属性,则实体还需要了解密码策略,以及它如何知道密码策略?更改密码策略是否需要更改每个实体?是否可以有两个具有不同密码策略的实体,等等

或者,您的系统中可能有一个拥有密码策略的聚合。在这种情况下,该聚合可能还负责所有密码(无论如何,所有密码都在该策略的范围内)。也就是说,密码可能实际上是字典中的一个值,而不是实体的属性,在字典中您可以使用entityId来查找它

在你无所不在的语言中挖掘,并与你的领域专家一起审查需求,这是你的工作重点。不要羞于确保你对你的dom有一个透彻的了解