samedi 27 février 2016

Converting DTO to Entity Subclass

My current situation is that I have an abstract model, in which many other models inherit from. The API consumes a "DTO" object, that is essentially one big object that contains all of the possible fields for each subclass, due to the client calling the service. I've created a solution for this, but it feels like it has some bad "smells". Here is an example of the setup:

public abstract class Person {
    public Long id;
    public Long name;
}

public class Employee extends Person {
    public Date startDate;
}

public class Student extends Person {
    public Double gpa;
}

public class PersonDTO {
    public Long id;
    public String name;
    public Date startDate;
    public Double gpa;
    public String type;
}

I want to convert the DTO to its concrete subclass with initialization. I've solved that by creating a factory and overriding a method in each of the subclasses:

public class PersonFactory {

    public Person getInstance(String type) {
        if (type.equals("Student")) {
            return new Student();
        } else if (type.equals("Employee")) {
            return new Employee();
        }

        return new Person();
    }
}

public class Student extends Person {

    public Double gpa;

    /**
     * Initializes the additional fields in the subclass
     * from the PersonDTO.
     */
    @Override
    public void initialize(PersonDTO dto) {
        this.gpa = dto.gpa;
    }
}

So the calling class will do the following:

public Person createPerson(PersonDTO dto) {
    Person person = new PersonFactory().getInstance(dto.type);
    person.name = dto.name;
    person.id = dto.id;
    person.initialize(dto);
}

To put this into perspective a little more, the model superclass (in this example, Person) has about 15 fields, and the subclasses can have up to 5 or 6, making using a constructor cumbersome. I feel like having the initialize method is a bad practice, and couples the Person class to the DTO class (which I'd like neither class to know about the other).

Ideally, I'd like to create a Mapper or Translator class, that will translate the DTO into the concrete subclass, but I keep running into the following issue with initializing the common fields:

public class PersonMapper implements Mapper<PersonDTO, Person> {

    public Person map(PersonDTO dto) {
        if (person.type.equals("Student")) {
            Student person = new Student();
            setupCommonFields(person, dto);   // required to call this method
            person.gpa = dto.gpa;             // inside of every block
            return person;
        } else if (person.type.equals("Employee")) {
            Employee person = new Employee();
            setupCommonFields(person, dto);
            person.startDate = dto.startDate;
            return person;
        }

        return person;
    }

    private void setupCommonFields(Person person, PersonDTO dto) {
        person.id = dto.id;
        person.name = dto.name;
    }
}

This seems like a clearly simple problem to solve, but I can't seem to come up with the most perfect solution. Is there a better-designed solution for this?

Aucun commentaire:

Enregistrer un commentaire