考虑以下类别:
@Getter
public class EmailVO {
private final Long id;
private final String firstName;
private final String email;
private final String address;
@Slf4j
@Component
@Scope("prototype")
public static class Builder {
private Lead lead;
private Long id;
private String firstName;
private String email;
private String address;
public Builder fromLead(Lead lead) {
this.lead = lead;
return this;
}
public EmailVO build() {
if (lead == null) {
log.error("Failed to build EmailVO: lead was not initialized");
return new EmailVO(this);
}
User user = lead.getUser();
id = user.getId();
firstName = user.getFirstName();
email = user.getEmail();
address = user.getAddress();
return new EmailVO(this);
}
}
private EmailVO(Builder builder) {
id = builder.id;
firstName = builder.firstName;
email = builder.email;
address = builder.address;
if (id == null ||
firstName == null ||
email == null ||
address == null)
{
throw new IllegalStateException(); // Maybe some ohter Unchecked Exception would be better
}
}
}
据我所知,这将是一个适当的VO类实现,它仅允许从其构建器构建新实例,该实例也充分遵循了构建器模式(如果我错了,请纠正我)。
从SOLID的角度来看,此代码很好,因为构建器的单一职责是聚集数据以构建
EmailVO
,并且其构造器将负责使仅有效的实例生效。现在,如果您关心代码的混乱和可读性(假设情况下的VO更大),则当构建器尝试在未初始化所需参数的情况下尝试构建时,构建器可能会失败,而不是让对象的构造函数失败,从而可能删除内部的许多null检查构造函数。在示例代码中,如果
lead
字段为null
,则此验证可能引发异常,而不是让EmailVO
的构造函数检查完整性,尽管这是构造函数的责任。可以从
EmailVO
的构造函数中删除验证,然后让构建器来处理它吗?考虑到在这种情况下,构造函数为private
使其对此类外部不可见。尽管如果构建器未验证必需的参数,它可能会失败,这似乎违反了SOLID,这是其责任之一,那就是聚合必需的数据以构建
EmailVO
实例。但是,一个让我大吃一惊的想法是将一个标志用作
EmailVO.Builder
类的成员字段,以表示是否成功聚集了必需的参数,然后EmailVO
的构造函数只能检查(并信任)该标志。 最佳答案
考虑到您的EmailVO
仅用于保存值,
我建议您最小化设置器和构造器中的登录名(@Christopher Schneider注释的变体)。
如果要验证
您可以将验证方法添加到EmailVO
对象。
关于“错误值生成”问题,将验证添加到生成器。EmailVO
验证可能包括类似“这是有效构造的电子邮件地址”之类的内容。
另外,不要将构建器传递给EmailVO
对象,只需具有该构建器使用的程序包访问构造器即可。确保构建器与EmailVO
对象位于同一程序包中。