问题
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
回答1:
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
回答2:
That's quite a complex question. I'd resolve this in few steps.
Parent
class should have some kind of interface that would allow your factory to recognize, what data should yourDTO
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]; } }
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.
You
could
do your factory method static, but if you want to avoid that, you have to useDependency 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.
回答3:
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();
}
}
来源:https://stackoverflow.com/questions/30025483/nice-way-of-factory-method-pattern-with-inheritance