2015-05-29 92 views
4

我开发包含几个层的应用程序。我们有返回模型对象的DAO层。我们也有映射器实例化DTO对象并将它们发送给客户端。实体映射到控制器层中的DTO。我在几个实体类中引入了继承。让我们假设在图像某事像下面多态性和DTO对象创建

class diagram (not enough reputation points to past image directly)

我问DAO动物的名单从具体的动物园。然后我得到List动物列表,但它们是具体的类型,因为动物是抽象的,我们不能在数据库中拥有Animal。我想从这个模型对象创建DTO。我不得不使用映射器在我,如果.. 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与所有可能的属性

回答

0

我不确定你是否真的有一个领域模型 - 他们真的有领域逻辑,还是他们只是数据容器?

无论如何,这种映射逻辑应该位于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中,你似乎没有扩展方法,但你也可以编写普通的静态类。见here。而且它似乎也有类似automapper for java

+0

它们相当容器。你是否推荐使用'instanceof'逻辑并按类型创建适当的DTO? – soulcoder

+0

我个人并不认为这有什么问题。我添加了一个例子。 –

+0

目前我有几乎相同的解决方案,因为你已经发布。但是我想以更好的方式做到这一点,以避免在添加新的动物类型和新的DTO类型时添加新的instanceof案例和类型检查。我想了一会儿,创建了一些工厂,使用instanceof似乎是最好的方法。完美的是迭代动物列表并调用一些接口/抽象方法来创建适当的DTO。 – soulcoder

0

使用instanceof您无法使用代理处理正在处理的对象。当你使用像Hibernate这样的ORM解决方案时,这可能是一个问题,因为你总是需要从数据库中读取实例的完整状态,以确保在需要时可以在用例中检查它的类型。

一个替代的将是声明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运营商的(这意味着你一般获得更多的面向对象编程的灵活性,例如使用代理,装饰器等)。

由于AnimalType很可能将是一个enum,那么你也可以得到你的DTO工厂和类似的地方,这是一个有点比if...else ifinstanceof更具可读性使用它在switch报表的能力。

+0

感谢您的回答。在我的继承结构中,我使用与getType几乎相同的鉴别器。我可以将列映射到属性,而不必在每个类中重写getType方法。但我仍然需要检查某处的类型并创建适当的DTO;) – soulcoder

2

您想为不同类型的对象选择不同的转换。基本上有两种方法解决这个问题在Java中:

  1. 使用instanceof或从资料库得到它映射对象到一个专门的Transformer对象的类(可以是简单地图<类,主变差动>) 。这是框架建议here使用的方法。

  2. 使用访客模式。这种方法的想法是,最简单的方法是在所有域对象中都有toDTO()方法,但这会在DTO的域对象中引入不需要的依赖项。另一种方法是将所有这些类放在一个对象中,但不幸的是,Java中的单一调度功能意味着要使用它,您仍然必须找出要变换的对象的类型并调用正确的方法:虚拟机无法为您做到这一点。访问者模式是解决这个问题的设计模式。

访问者模式的基本设计是这样的:

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

} 

当你介绍一个新的动物类型,你需要一个新的visit方法添加到AnimalVisitor类。您将被提示执行此操作,因为您必须在新的动物类型中实施accept方法。这也会提示您更新DTOTransformer以及AnimalVisitor的任何其他实施。

正如你所看到的,这种方法需要更多的代码,然后是简单的instanceof风格的方法。它确实为您提供了一个很好的可扩展机制,用于其他类似的操作,您希望在您的域中执行此操作。

我想这真的取决于你的情况哪种方法最好。但是,我总是推荐使用像DozerModdelMapper这样的框架来自己编写if (.. instanceof ...) else if (...

+0

感谢您的回答,我考虑过第二种选择,但是按照您的说法,它将模型层与通信层耦合在一起,甚至比在我看来使用instanceof更糟糕。随着变换器的地图,我还需要填充数据(添加键/类和适当的映射器)。但也许它会看起来更好,并且会更加可重用。我会考虑的。 – soulcoder

+0

@ user3201879编辑完成后请重新阅读我的答案。 –

+1

@ user3201879使用访问者模式可以很好地分离各层。 –