工厂
当创建一个对象或创建整个聚合时,如果创建工作很复杂,或者暴露了过多的内部结构,则可以使用工厂进行封装。
为什么需要工厂
当创建一个复杂对象或聚合的过程很复杂并且暴露出了过多的内部结构时,我们则可以使用工厂进行封装。一个对象在它的生命周期中要承担大量的职责,如果再让复杂对象负责自身的创建,那么职责过载将会导致问题。
我们设计好领域模型供客户方调用,但如果客户方也必须使用如何装配这个对象,则必须知道对象的内部结构。好比你去驾校学车,却得先学会发动机的原理。对客户方开发来说这是很不友好的。其次,复杂对象或者聚合当中的领域知识(业务规则)需要得到满足,如果让客户方自己装配复杂对象或聚合的话,就会将领域知识泄露到客户方代码中去。
对象的创建本身可以是一个主要操作,但被创建的对象并不适合承担复杂的装配操作。将这些职责混在一起可能产生难以理解的拙劣设计。让客户直接负责创建对象又会使客户的设计陷入混乱,并且破坏被装配对象或聚合的封装,而且导致客户与被创建对象的实现之间产生过于紧密的耦合。
工厂创建要点
- 每个创建方法都是原子的,而且要保证被创建对象或聚合的所有固定规则。 工厂生成的对象要处于一致的状态。在生成实体时,这意味着创建满足所有固定规则的整 个聚合,但在创建完成后可以向聚合添加可选元素。在创建不变的值对象时,这意味着所有属性必须被初始化为正确的最终状态。如果工厂通过其接口收到了一个创建对象的请求,而它又无法正确地创建出这个对象,那么它应该抛出一个异常,或者采用其他机制,以确保不会返回错误的值。
- 工厂应该被抽象为所需的类型,而不是所要创建的具体类。
代码示例
/**
* 用户工厂
*
* @author haoxin
* @date 2021-02-24
**/
public class UserFactory {
private UserRepository userRepository;
public UserFactory(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(Mobile mobile, Email email, Password password, UserName userName, List<RoleId> roleIdList, TenantId currentTenantId) {
List<User> users = userRepository.find(mobile);
Account account;
if(users != null && !users.isEmpty()) {
for(User user : users) {
if(user.getTenantId().sameValueAs(currentTenantId)) {
throw new RuntimeException("租户内账号已存在");
}
}
account = users.get(0).getAccount();
} else {
account = new Account(mobile, email, password);
}
if(roleIdList == null || roleIdList.isEmpty()) {
throw new RuntimeException("角色未分配");
}
return new User(userName,account,roleIdList);
}
}