简体   繁体   English

多态性和DTO对象创建

[英]Polymorphism and DTO object creation

I develop application which contains few tiers. 我开发的应用程序包含几层。 We have DAO layer which returns model objects. 我们有DAO层,它返回模型对象。 We also have mappers which instantiate DTO objects and send them to clients. 我们也有实例化DTO对象并将其发送给客户端的映射器。 Entities are mapped to DTOs in Controller layer. 实体映射到控制器层中的DTO。 I've introduced inheritance in few entity classes. 我在几个实体类中介绍了继承。 Let's assume sth like on image below 让我们假设像下面的图片一样

class diagram (not enough reputation points to past image directly) 类图(没有足够的声誉直接指向过去的图像)

I ask DAO for list of animals from the concrete ZOO. 我要求DAO列出具体动物园的动物清单。 Then I get list List animals, but they are of concrete type because Animal is abstract and we cannot have just Animal in the database. 然后我得到了列表动物列表,但是它们是具体类型的,因为动物是抽象的,我们不能只在数据库中有动物。 I would like to create DTOs from this model objects. 我想从此模型对象创建DTO。 I have to use mapper in which I have if .. else statements checking type of each animal and then creating proper DTO, sth like 我必须使用mapper,如果我有if .. else语句,则检查每个动物的类型,然后创建适当的DTO,例如

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

This code does not look nice. 这段代码看起来不太好。 It would be nice to use polymorphism and call some method on each animal to produce DTO, but it is bad to have logic in domain model creating DTO objects just to communication. 使用多态性并在每只动物上调用某种方法来产生DTO会很好,但是在域模型中创建仅用于通信的DTO对象的逻辑就不好了。 How do you resolve such situations? 您如何解决这种情况?

Edit: To be more specific, I want to have DTO like 1. DogDTO which contains only fields color and name 2. FishDTO which contains only numberOfFins Not one big AnimalDTO with all possible attributes 编辑:更具体地说,我想拥有DTO,例如1. DogDTO仅包含颜色和名称字段2. FishDTO仅包含numberOfFins不是一个具有所有可能属性的大AnimalDTO

You want to select a different transformation for a different type of object. 您想为不同类型的对象选择不同的变换。 Basically there are two approaches to this problem in Java: 在Java中,基本上有两种方法可以解决此问题:

  1. Use the instanceof or map the Class of the object to a specialized Transformer object by getting it from a repository (could be simple Map<Class, Transfomer>). 通过从存储库中获取对象,可以使用instanceof或将对象的类映射到专用的Transformer对象(可以是简单的Map <Class,Transfomer>)。 This is the approach that the frameworks suggested here use. 这是此处建议的框架使用的方法。

  2. Use the visitor pattern. 使用访客模式。 The idea of this approach is that the simplest approach would be to have a toDTO() method in all of your domain objects, but this would introduce an unwanted dependency in your domain objects on your DTO's. 这种方法的想法是,最简单的方法是在所有域对象中都具有toDTO()方法,但这会在域对象中对DTO引入不必要的依赖性。 Another approach would be to have all these classes in a single object toDTO(Dog d) toDTO(Fish f) , but unfortunately the single-dispatch feature in Java means that to be able to use this, you would still have to figure out the type of the object you want to transform and call the right method: the VM cannot do this for you. 另一种方法是将所有这些类都放在单个对象toDTO(Dog d) toDTO(Fish f) ,但是不幸的是,Java中的单调度功能意味着要使用此功能,您仍然必须找出要转换的对象的类型并调用正确的方法:VM无法为您执行此操作。 The Visitor pattern is a design pattern that solves this problem. 访客模式是解决此问题的设计模式。

The basic design of the visitor pattern is like this: 访客模式的基本设计如下:

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);
  }

}

When you introduce a new Animal type, you need to add a new visit method to the AnimalVisitor class. 引入新的Animal类型时,您需要向AnimalVisitor类添加新的visit方法。 You will be prompted to do this, since you will have to implement the accept method in the new Animal type. 系统将提示您执行此操作,因为您必须在新的Animal类型中实现accept方法。 This will also prompt you to update the DTOTransformer and any other implementations of the AnimalVisitor . 这也将提示您更新DTOTransformer和任何其他实现AnimalVisitor

As you can see, this approach requires a lot more code then the simple instanceof style approach. 如您所见,此方法比简单的instanceof样式方法需要更多的代码。 It does provide you with a nice extendable mechanisme for other similar operations you would want to do on your domain. 它确实为您提供了很好的可扩展机制,可用于您希望在域上执行的其他类似操作。

I guess it really depends on your situation which approach is best. 我想这真的取决于您的情况,哪种方法最好。 I would, however, always recommend using a framework like Dozer or ModdelMapper over writing the if (.. instanceof ...) else if (... myself. 但是,我总是建议使用DozerModdelMapper之类的框架来代替 if (.. instanceof ...) else if (...我自己。

I'm not sure if you really have a domain model - do they actual have domain logic, or are they just data containers? 我不确定您是否真的有域模型-它们是否确实具有域逻辑,或者它们仅仅是数据容器?

Anyway, this mapping logic should be in the outer layer where the DTO's are exposed, not in the domain layer. 无论如何,这种映射逻辑应该在DTO暴露的外层,而不是域层。 The whole idea is to make domain not dependent on an outer layer. 整个想法是使域不依赖于外层。

Just create mappers (for example as extension methods) in your controller layer that can be reused. 只需在控制器层中创建可重复使用的映射器(例如,作为扩展方法)。 Example: 例:

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 };
    }
}

Usage: 用法:

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

Or optionally use AutoMapper to do the mappings for you. 或选择使用AutoMapper为您完成映射。

EDIT: noticed that you just added JAVA tag, while my code was a C# example. 编辑:注意到您刚刚添加了JAVA标记,而我的代码是一个C#示例。 In java it seems you don't have extensions methods, but you can write normal static classes also. 在Java中,似乎没有扩展方法,但您也可以编写普通的静态类。 See here . 这里 And it seems there's also something like automapper for java . 而且似乎还有类似java的automapper之类的东西。

With instanceof you can't use proxies for the objects you're working with. 使用instanceof您不能为正在使用的对象使用代理。 That can be a problem when you use ORM solutions like Hibernate, because you would always need to read the full state of the instance from DB just to be sure that you can check its type in use cases when you need to. 当您使用诸如Hibernate之类的ORM解决方案时,这可能是一个问题,因为您始终需要从DB读取实例的完整状态,以确保可以在需要时在用例中检查其类型。

One of the alternatives would be to declare getType() method and override it in each subclass: 一种选择是声明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;
   }
}

This way you get rid of the instanceof operator (meaning you get more OOP flexibility in general, for example to use proxies, decorators, etc). 这样,您就可以摆脱instanceof运算符(这意味着您通常会获得更大的OOP灵活性,例如使用代理,装饰器等)。

Since AnimalType will probably be an enum , then you also get the ability to use it in switch statements in your DTO factories and similar places, which is a little bit more readable than if...else if with instanceof . 由于AnimalType可能是一个enum ,因此您还可以在DTO工厂和类似场所的switch语句中使用它,这比if...else if instanceof更具可读性。

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

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