根据类型层次结构创建映射函数

Create a mapping function depending on type hierarchy

我有一个遵循 领域驱动设计 规则的应用程序。 DDD的规则之一是领域层不应该依赖于基础设施层。我有一个工具可以检查从域到基础架构是否存在任何依赖项 (imports)。 在我的基础架构层中,我有一个 class 层次结构,如下所示:

在我的领域层,像这样:

现在,我需要将我的域 classes 映射到基础设施实体,以便将它们保存在我的数据库中。

我的域中有一个输出端口,它是一个接口:

// infra/ports/out/RuleRepository.java
public interface RuleRepository {
  Rule save(Rule rule);
}

该接口在基础设施层实现:

// domain/RuleRepositoryJpaAdapter.java
public class RuleRepositoryJpaAdapter implements RuleRepository {

  // extends CrudRepository<RuleEntity, Long>
  RuleCrudRepository jpaRepository;
  RuleMapper mapper;

  @Override
  public Rule save(Rule rule) {
    return jpaRepository.save(mapper.mapToEntity(rule))
      .mapToDomain();
  }
}

我正在寻找一种无需检查规则类型即可实现 mapToEntity 的方法。最好的方法是向 RuleIpRuleBlackListRule 添加一个 mapToEntity 方法,但这会破坏检查域和域之间是否存在任何导入的单元测试基础设施层。如果还有其他办法?

我现在拥有的:

public class RuleMapper {

  public RuleEntity mapToEntity(Rule rule) {
    if (rule instanceof IpRule) {
      return new IpRuleEntity().mapFromDomain(rule);
    } else if (rule instanceof BlackListRule) {
      return new BlackListRuleEntity().mapFromDomain(rule);
    } else {
      throw new IllegalArgumentException("Unexpected argument of type " + (rule == null ? "null" : rule.getClass().getName()));
    }
  }

}

您可以使用类似 Visitor Pattern to implement double dispatch 的东西。

在这个例子中,它看起来像这样:

public abstract class Rule {
    // Maps a rule to a result type T
    public interface Mapper<T> {
        T mapIpRule(IpRule rule);
        T mapBlackListRule(BlackListRule rule);
    }

    public abstract <T> T map(Mapper<T> mapper);
    
    // ...
}

public class IpRule extends Rule {
    @Override
    public <T> T map(Mapper<T> mapper) {
        return mapper.mapIpRule(this);
    }

    // ...
}

public class BlackListRule extends Rule {
    @Override
    public <T> T map(Mapper<T> mapper) {
        return mapper.mapBlackListRule(this);
    }

    // ...
}

public class RuleMapper implements Rule.Mapper<RuleEntity> {

  public RuleEntity mapToEntity(Rule rule) {
    return rule.map(this);
  }

  @Override
  public RuleEntity mapIpRule(IpRule rule) {
    return new IpRuleEntity();
  }

  @Override
  public RuleEntity mapBlackListRule(BlackListRule rule) {
    return new BlackListRuleEntity();
  }
}

它有一个很好的 属性 编译时检查是否正确处理了所有子类型。如果以后添加新的Rule子类型,则需要实现map方法,这就需要在Rule.Mapper接口中添加一个方法,进而需要RuleMapper 实现该方法。在使用运行时类型检查 instanceof 的问题中给出的示例中,可能会遗漏一个案例,导致运行时出现 IllegalArgumentException

但是,您需要判断额外的复杂性对于您的具体情况是否值得。可能您现有的 RuleMapper 就可以了。