繁体   English   中英

多态性和DTO对象创建

[英]Polymorphism and DTO object creation

我开发的应用程序包含几层。 我们有DAO层,它返回模型对象。 我们也有实例化DTO对象并将其发送给客户端的映射器。 实体映射到控制器层中的DTO。 我在几个实体类中介绍了继承。 让我们假设像下面的图片一样

类图(没有足够的声誉直接指向过去的图像)

我要求DAO列出具体动物园的动物清单。 然后我得到了列表动物列表,但是它们是具体类型的,因为动物是抽象的,我们不能只在数据库中有动物。 我想从此模型对象创建DTO。 我必须使用mapper,如果我有if .. else语句,则检查每个动物的类型,然后创建适当的DTO,例如

if (animal instanceof Dog) {
  .. create dog dto
} else if (animal instance of Cat) {
  .. create cat dto
} .. and so on

这段代码看起来不太好。 使用多态性并在每只动物上调用某种方法来产生DTO会很好,但是在域模型中创建仅用于通信的DTO对象的逻辑就不好了。 您如何解决这种情况?

编辑:更具体地说,我想拥有DTO,例如1. DogDTO仅包含颜色和名称字段2. FishDTO仅包含numberOfFins不是一个具有所有可能属性的大AnimalDTO

您想为不同类型的对象选择不同的变换。 在Java中,基本上有两种方法可以解决此问题:

  1. 通过从存储库中获取对象,可以使用instanceof或将对象的类映射到专用的Transformer对象(可以是简单的Map <Class,Transfomer>)。 这是此处建议的框架使用的方法。

  2. 使用访客模式。 这种方法的想法是,最简单的方法是在所有域对象中都具有toDTO()方法,但这会在域对象中对DTO引入不必要的依赖性。 另一种方法是将所有这些类都放在单个对象toDTO(Dog d) toDTO(Fish f) ,但是不幸的是,Java中的单调度功能意味着要使用此功能,您仍然必须找出要转换的对象的类型并调用正确的方法:VM无法为您执行此操作。 访客模式是解决此问题的设计模式。

访客模式的基本设计如下:

public abstract class AnimalVisitor<T> {

  public abstract T visit (Dog d);

  public abstract T visit (Fish f);

  ...

}

public abstract class Animal {

  public <T> abstract T accept (AnimalVisitor<T> v);

  ...

}

public class Dog extends Animal {

  public <T> T accept (AnimalVisitor<T> v) {
    return v.visit(this);
  }

  ...

}

public class Fish extends Animal {

  public <T> T accept (AnimalVisitor<T> v) {
    return v.visit(this);
  }

  ...

}

public class DTOTransformer extends AnimalVisitor<DTO> {

  public DogDTO visit (Dog d) {
    return new DogDTO(d);
  }

  public FishDTO visit (Fish f) {
    return new FishDTO(f);
  }

}

引入新的Animal类型时,您需要向AnimalVisitor类添加新的visit方法。 系统将提示您执行此操作,因为您必须在新的Animal类型中实现accept方法。 这也将提示您更新DTOTransformer和任何其他实现AnimalVisitor

如您所见,此方法比简单的instanceof样式方法需要更多的代码。 它确实为您提供了很好的可扩展机制,可用于您希望在域上执行的其他类似操作。

我想这真的取决于您的情况,哪种方法最好。 但是,我总是建议使用DozerModdelMapper之类的框架来代替 if (.. instanceof ...) else if (...我自己。

我不确定您是否真的有域模型-它们是否确实具有域逻辑,或者它们仅仅是数据容器?

无论如何,这种映射逻辑应该在DTO暴露的外层,而不是域层。 整个想法是使域不依赖于外层。

只需在控制器层中创建可重复使用的映射器(例如,作为扩展方法)。 例:

public class AnimalDto
{
    public string Sound { get; set; }
}

public class CatDto : AnimalDto
{

}

public class DogDto : AnimalDto
{
    public bool IsTrained {get; set;}
}

public class AnimalDo
{
    public string Sound { get; set; }
}

public class CatDo : AnimalDo
{        
}

public class DogDo : AnimalDo
{
    public bool IsTrained {get; set;}
}

public static class MappingExtensions
{
    public static AnimalDto Map(this AnimalDo animalDo)
    {
        if (animalDo is CatDo) return (animalDo as CatDo).Map();
        if (animalDo is DogDo) return (animalDo as DogDo).Map();
        return null;
    }

    public static DogDto Map(this DogDo dogDo)
    {
        return new DogDto() { IsTrained = dogDo.IsTrained, Sound = dogDo.Sound };
    }
}

用法:

AnimalDo animal = new DogDo() { IsTrained = true, Sound = "bwarf"};
var dogDto = animal.Map();

或选择使用AutoMapper为您完成映射。

编辑:注意到您刚刚添加了JAVA标记,而我的代码是一个C#示例。 在Java中,似乎没有扩展方法,但您也可以编写普通的静态类。 这里 而且似乎还有类似java的automapper之类的东西。

使用instanceof您不能为正在使用的对象使用代理。 当您使用诸如Hibernate之类的ORM解决方案时,这可能是一个问题,因为您始终需要从DB读取实例的完整状态,以确保可以在需要时在用例中检查其类型。

一种选择是声明getType()方法并在每个子类中重写它:

abstract class Animal {
   abstract AnimalType getType();
}

class Dog extends Animal {
   @Override
   AnimalType getType() {
      return AnimalType.DOG;
   }
}

class Cat extends Animal {
   @Override
   AnimalType getType() {
      return AnimalType.CAT;
   }
}

这样,您就可以摆脱instanceof运算符(这意味着您通常会获得更大的OOP灵活性,例如使用代理,装饰器等)。

由于AnimalType可能是一个enum ,因此您还可以在DTO工厂和类似场所的switch语句中使用它,这比if...else if instanceof更具可读性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM