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

Posted

技术标签:

【中文标题】根据类型层次结构创建映射函数【英文标题】:Create a mapping function depending on type hierarchy 【发布时间】:2021-12-06 09:22:29 【问题描述】:

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

在我的领域层,像这样:

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

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

// 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()));
    
  


【问题讨论】:

【参考方案1】:

您可以使用Visitor Pattern 之类的东西来实现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 就可以了。

【讨论】:

这是额外的复杂性,但这允许我创建新类型而不必担心忘记在映射器中添加 else if 语句。因为不实现 map 方法就无法创建新的 Rule 子项,对吧?

以上是关于根据类型层次结构创建映射函数的主要内容,如果未能解决你的问题,请参考以下文章

如何创建类型化工厂方法构造函数的类层次结构并使用抽象类型从 Scala 访问它们?

如何将实体映射到包含层次结构的维度表?

如何创建参数化递归 CTE 以展平标量函数中的层次结构?

具有虚拟性的多级层次结构可防止转换函数指针

在Prolog中定义包含函数类型的类型层次结构

java 实体类 注解 继承问题!