设计模式 - 如何仅在某些情况下强制执行对象属性(构建器模式,依赖注入)

Posted

技术标签:

【中文标题】设计模式 - 如何仅在某些情况下强制执行对象属性(构建器模式,依赖注入)【英文标题】:Design patterns - How to enforce object attributes only in some situations (Builder pattern, Dependency Injection) 【发布时间】:2016-07-09 13:11:27 【问题描述】:

我在编写其中一个类时处于一种非常特殊的情况。我有一个名为User 的类,看起来像这样:

public class User 
    private long id; // + getters and setters
    private boolean isDeletable; // + getters and setters
    private String name; // + getters and setters
    private String password; // + getters and setters
    private String email; // + getters and setters
    private String authenticationRealm; // + getters and setters
    private String displayName; // + getters and setters
    private Date deletedDate; // + getters and setters

在我的代码中有几种情况,我只需要 User 类型的空对象,因此只需使用默认构造函数构建它:new User()

但是,我有另一个名为 CreateUserRequest 的类,它模拟 REST 请求以在服务器中创建用户。 最小负载必须包含以 JSON 格式发送的 namepasswordemailauthenticationRealm 属性。

现在我通过在请求的构造函数中检查这些参数来处理这个问题:

public CreateUserRequest(User user) 
    if(user.getName() == null || user.getPassword() == null || user.getEmail() == null || user.getAuthenticationRealm() == null)
        throw new RuntimeException("Not enough attributes in User object. Minimum: name, password, e-mail and authentication realm.");

这工作正常,但有些东西很痒...我想以更安全的方式强制执行此操作,以便代码强制填充属性,而不会引发异常。

我觉得必须有更好的方法来使用设计模式来做到这一点。我想创建一个UserRequestBuilder 类,但这也可能意味着在build() 方法中抛出异常(否则,有没有办法可以保证在build() 之前填充属性?)。依赖注入听起来也是一种可能性,但我不确定在这个特定的例子中我将如何把它放在适当的位置......

有什么想法吗?

【问题讨论】:

听起来像是一个不同的对象,一个 CreateUser 对象可能有一个构造函数并验证提供的值。由于它们执行两个不同的任务,因此它们必须基于同一类有什么理由吗? 我的直接想法是在 User 类上添加一个新的 isValidUserRequest() 方法。认为 User 类应该自行决定它是否有效用于请求。 感谢@dbugger 的评论。是的 - 多个 REST 调用生成或使用具有与 User 类完全相同的属性的 JSON 对象(即 User 类表示远程实体)。这些类并没有真正执行不同的任务。 CreateUserRequest 类构建一个 REST 调用,该调用以 JSON 格式在其有效负载中传递一个 User 对象。 @LawrenceTierney 感谢您的评论。这说得通。但是我会遇到同样的问题 - 如果isValidUserRequest() 返回 false 怎么办?我仍然需要以某种方式对待它。理想情况下,我想要一种只有在所有属性都存在的情况下才能创建(或构建)用户请求的方式。 它们可以实现相同的抽象,IUser 或 UserBase,所以它们在外面看起来很相似,但在内部有不同的好处。 【参考方案1】:

让您的 REST 服务在 UserDTO 上运行怎么样? (当然,UserDTO 可以替换为 User 的子类)。

您可以在 UserDTO 上使用 @NonNull 注释字段、setter 或构造函数参数,并在将空值而不是名称密码、电子邮件等传递给 UserDTO 时让 Checker Framework 发出编译器警告。

使用像Mapstruct 这样的框架,REST 服务 DTO 和后端对象之间的映射非常简单:

@Mapper
public interface UserMapper 

    public static final UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    UserDTO map(User user);

    User map(UserDTO userDTO);

上面的代码将在编译时生成一个 UserMapper 实现,自动生成的代码用于指定的方法( - 并且自动生成的代码只是简单地将名称相似的 getter 和 setter 配对。您可以自己执行此操作,但使用许多 DTO/实体会变得耗时和无聊)。

在 DTO 中,您可以排除所有不想公开的字段。

附言。我自己对上面提到的用法是这样的:我正在创建一个基于 Jersey 的 REST 服务器,即 JAX-RS 的参考实现。这个项目,称为 A,只知道 DTO。 REST 方法调用另一个项目 B,该项目从数据库中检索对象,并将它们映射到相应的 DTO,然后将其返回给项目 A。这种模式的部分原因是项目 B 的实体由于历史原因是杂乱无章的方法/功能,不应该暴露给项目 A。 至于健全性检查(JSON 到 DTO),jersey 支持 Bean Validation,也就是说,如果它们被注释,框架将验证每个 rest 资源的输入 bean与@Valid。 也可以创建自己的自定义注释,其中定义了 ConstraintValidator。 bean 验证框架将检查带注释的球衣 REST 方法参数上的这些约束。 见https://jersey.java.net/documentation/latest/bean-validation.html#d0e13690

【讨论】:

非常有趣的方法,@Hervian。如果可行,我将尝试并将其标记为答案。谢谢。 我采用了一种非常相似的方法,它奏效了。谢谢。【参考方案2】:

我遇到了一个类似的问题,我想出了添加带有参数的构造函数的 Custombuilder 的解决方案。因此,它确保我确保客户端(用户)必须提供这些凭据来构建该对象

    class UserRequestBuilder
          public UserRequestBuilder(String name,String password,String email,String authenticationRealm)
// set values here


【讨论】:

以上是关于设计模式 - 如何仅在某些情况下强制执行对象属性(构建器模式,依赖注入)的主要内容,如果未能解决你的问题,请参考以下文章

如何设计一个仅在其中一个部分使用 CUDA 的库,以便其他部分在没有安装 CUDA 的情况下也可以工作?

如何仅在某些情况下禁用 Django 的 csrf 保护?

如何强制杰克逊在控制器中调用对象(dto)中所有属性的 Set 方法?

如何在 AVPlayer 中强制横向模式?

如何在变量声明 (CS0270) 中强制执行数组的长度?

响应式设计损坏(仅在 iPhone 上)- 可以使用 javascript 强制执行媒体查询吗?