简体   繁体   中英

Nice way of factory method pattern with inheritance

Suppose I have following class hierarchy:

class abstract Parent{}

class FirstChild extends Parent {}

class SecondChild extends Parent {}

And I'd like to create DTO objects from each child:

class abstract ParentDTO {}

class FirstChildDTO extends ParentDTO{}

class SecondChildDTO extends ParentDTO{}

I think I need a factory method something like this:

ParentDTO createFrom(Parent source); 

Is there any nice way to do this in Java without instanceof checks and if/else statements?

EDIT : This factory method does not work:

public ParentDTO create(Parent source)
{
    return _create(source);
}

private FirstChildDTO _create(FirstChild source)
{
    return new FirstDTO();
}

private SecondChildDTO _create(SecondChild source)
{
    return new SecondDTO();
}

private ParentDTO _create(Parent source)
{
    return new ParentDTO();
}

It only generates ParentDTO s and here is why:

Parent p = new FirstChild();
System.out.println(factory.create(p));  //ParentDTO

FirstChild f = new FirstChild();
System.out.println(factory.create(f));  //FirstChildDTO

If you insist on using a factory for DTO creation you can use simple method overloading. Example follows:

public class Factory {

    public ParentDTO createDTO(Parent parent) {
        return new ParentDTO();
    }
    public FirstChildDTO createDTO(FirstChild firstChild) {
        return new FirstChildDTO();
    }
    public SecondChildDTO createDTO(SecondChild SecondChild) {
        return new SecondChildDTO();
    }
}

public class Parent {
}

public class FirstChild extends Parent {
}

public class SecondChild extends Parent {
}

public class ParentDTO {

    @Override
    public String toString() {
        return this.getClass().getSimpleName();
    }
}

public class FirstChildDTO extends ParentDTO {

    @Override
    public String toString() {
        return this.getClass().getSimpleName();
    }
}

public class SecondChildDTO extends ParentDTO {

    @Override
    public String toString() {
        return this.getClass().getSimpleName();
    }
}

public class App {

    public static void main(String[] args) {
        Factory factory = new Factory();
        ParentDTO parentDTO = factory.createDTO(new Parent());
        System.out.println(parentDTO);
        FirstChildDTO firstChildDTO = factory.createDTO(new FirstChild());
        System.out.println(firstChildDTO);
        SecondChildDTO secondChildDTO = factory.createDTO(new SecondChild());
        System.out.println(secondChildDTO);
    }
}

Running the App as Java application in my IDE outputs:

ParentDTO
FirstChildDTO
SecondChildDTO

That's quite a complex question. I'd resolve this in few steps.

  1. Parent class should have some kind of interface that would allow your factory to recognize, what data should your DTO object contain, like this (in PHP language):

     class Parent { abstract function getDataFields(); } class FirstChild extends Parent { function getDataFields() { return ['id' => $this->id, 'name' => $this->name, 'email' => $this->email]; } } class SecondChild extends Parent { function getDataFields() { return ['id' => $this->id, 'brand' => $this->brand, 'model' => $this->model]; } } 
  2. Now, you only need a single factory, AND i would use a single class for DTO too, since you know what subclass of Parent

     class DTOFactory { function createFromParent(Parent $parent) { return new DTO($parent); } } class DTO { private $fields = []; function __construct(Parent $parent) { foreach ($parent->getDataFields() as $field => $value) { $this->fields[$field] = $value } } function __get($field) { return $this->fields[$field]; } } 

Of course, there are a lot of ways to make DTO's in various languages, i can't cover this in detail, that's up to you.

  1. You could do your factory method static, but if you want to avoid that, you have to use Dependency Injection of some sorts:

     class MyController { private $dtoFactory; function __construct(DTOFactory $factory) { $this->dtoFactory = $factory; } function returnDTOAction() { $object = new FirstChild(); // or some other way to get your child; return $this->factory->createFromParent($object); } } class DIContainer { private $instances = []; function get($className) { if (!is_object($this->instances[$className]) { $this->instances[$className] = new $className; } return $this->instances[$className] = new $className; } } $container = new DIContainer; $controller = new MyController($container->get('DTOFactory')); 

This is a very simple example of DI Container , better examples have automatic dependency resolution, so you simply call them like $container->get('MyController'); and get a class with automatically resolved dependencies, but i won't go into further detail on this, since their implementation is heavily language-dependent, and i only stuck with a basic one.

Anyways, hope this helps. If you need some clarifications - feel free to comment.

You can code the factory method directly into the model class and utilize Java's covariant return type to return the correct type of DTO, for example:

public class Parent {

    public ParentDTO createDTO() {
        return new ParentDTO();
    }
}


public class FirstChild extends Parent {

    @Override
    public FirstChildDTO createDTO() {
        return new FirstChildDTO();
    }
}


public class SecondChild extends Parent {

    @Override
    public SecondChildDTO createDTO() {
        return new SecondChildDTO();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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