[英]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中,基本上有两种方法可以解决此问题:
通过从存储库中获取对象,可以使用instanceof
或将对象的类映射到专用的Transformer对象(可以是简单的Map <Class,Transfomer>)。 这是此处建议的框架使用的方法。
使用访客模式。 这种方法的想法是,最简单的方法是在所有域对象中都具有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
样式方法需要更多的代码。 它确实为您提供了很好的可扩展机制,可用于您希望在域上执行的其他类似操作。
我想这真的取决于您的情况,哪种方法最好。 但是,我总是建议使用Dozer或ModdelMapper之类的框架来代替 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.