【问题标题】:Nice way of factory method pattern with inheritance带有继承的工厂方法模式的好方法
【发布时间】:2015-05-04 08:11:39
【问题描述】:

假设我有以下类层次结构:

class abstract Parent{}

class FirstChild extends Parent {}

class SecondChild extends Parent {}

我想从每个孩子创建 DTO 对象:

class abstract ParentDTO {}

class FirstChildDTO extends ParentDTO{}

class SecondChildDTO extends ParentDTO{}

我想我需要一个类似这样的工厂方法:

ParentDTO createFrom(Parent source); 

在没有instanceof 检查和 if/else 语句的情况下,在 Java 中是否有任何不错的方法?

编辑: 这个工厂方法不起作用:

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

它只生成ParentDTOs,原因如下:

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

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

【问题讨论】:

标签: java oop design-patterns factory factory-pattern


【解决方案1】:

如果您坚持使用工厂创建 DTO,您可以使用简单的方法重载。示例如下:

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

在我的 IDE 输出中将应用程序作为 Java 应用程序运行:

父DTO
FirstChildDTO
SecondChildDTO

【讨论】:

  • 这是我尝试的第一件事,但它不起作用。这种方式只会创建ParentDTO 实例。
  • 我刚试过这个,它似乎对我有用。我将编辑我的答案,为您提供详细信息。你能在你的机器上试试吗?
  • 是的,这很好用。我正在尝试另一个案例,我正在用它来更新问题。
【解决方案2】:

这是一个相当复杂的问题。我会分几步解决这个问题。

  1. Parent 类应该有某种接口,可以让你的工厂识别你的DTO 对象应该包含哪些数据,就像这样(在 PHP 语言中):

    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. 现在,您只需要一个工厂,而且我也会为 DTO 使用一个类,因为您知道 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];
        }
    }
    

当然,有很多方法可以制作各种语言的 DTO,我无法详细介绍,这取决于你。

  1. could 将您的工厂方法设为静态,但如果您想避免这种情况,您必须使用某种形式的 Dependency Injection

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

这是DI Container 的一个非常简单的示例,更好的示例具有自动依赖解析,因此您只需像$container->get('MyController'); 一样调用它们并获得一个具有自动解析依赖的类,但我不会对此进行详细介绍,因为它们的实现很大程度上依赖于语言,而我只坚持一个基本的。

无论如何,希望这会有所帮助。如果您需要一些说明,请随时发表评论。

【讨论】:

  • 用动态语言做得很好。也许我应该将问题限制在 Java :)
  • 我在 Java 方面没有那么丰富的经验,但我猜您使用 HashMap 作为您的 DTO 字段键值存储,仅此而已。而且,据我所知,Java 有很多 DI-Containers 实现,所以这也不是问题。
  • @Eternal1 Java 是严格类型化的,HashMap 并没有真正作为值对象的替代品引入。虽然您的建议可以在 Java 中轻松实现,但最好避免。
【解决方案3】:

您可以将工厂方法直接编码到模型类中,并利用Java的covariant return type返回正确的DTO类型,例如:

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

【讨论】:

  • 对不起,这个想法是解耦模型和 DTO。
  • 没问题。您应该选择最适合您的设计。另请查看我的其他答案。
猜你喜欢
  • 1970-01-01
  • 2020-10-25
  • 2013-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
相关资源
最近更新 更多