8
votes

I develop application which contains few tiers. We have DAO layer which returns model objects. We also have mappers which instantiate DTO objects and send them to clients. Entities are mapped to DTOs in Controller layer. 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. 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. I have to use mapper in which I have if .. else statements checking type of each animal and then creating proper DTO, sth like

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. 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

3

3 Answers

6
votes

You want to select a different transformation for a different type of object. Basically there are two approaches to this problem in 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>). 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. 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. 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. You will be prompted to do this, since you will have to implement the accept method in the new Animal type. This will also prompt you to update the DTOTransformer and any other implementations of the AnimalVisitor.

As you can see, this approach requires a lot more code then the simple instanceof style approach. 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.

0
votes

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. 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.

EDIT: noticed that you just added JAVA tag, while my code was a C# example. In java it seems you don't have extensions methods, but you can write normal static classes also. See here. And it seems there's also something like automapper for java.

0
votes

With instanceof you can't use proxies for the objects you're working with. 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.

One of the alternatives would be to declare getType() method and override it in each subclass:

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).

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.