Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java DDD-复合聚合序列化-设计问题_Java_Oop_Domain Driven Design_Composite_Visitor Pattern - Fatal编程技术网

Java DDD-复合聚合序列化-设计问题

Java DDD-复合聚合序列化-设计问题,java,oop,domain-driven-design,composite,visitor-pattern,Java,Oop,Domain Driven Design,Composite,Visitor Pattern,我正在尝试将DDD应用于一个Java项目。这就是我偶然发现的问题: 在域中,我有一个使用复合OOP模式实现的聚合。此聚合上的方法生成一些域对象,这些对象需要序列化并通过连接发送。这些是我考虑过的选择: 在我域的应用程序服务部分,我使用聚合,调用它的方法,并尝试将结果序列化到DTO。为了将其序列化为DTO,我必须使用instanceof检查当前节点是复合节点还是子节点,然后继续序列化。由于instanceof是代码气味(正如我读到它打破了打开/关闭原则等),我决定尝试使用访问者模式 为了应用Vis

我正在尝试将DDD应用于一个Java项目。这就是我偶然发现的问题:

在域中,我有一个使用复合OOP模式实现的聚合。此聚合上的方法生成一些域对象,这些对象需要序列化并通过连接发送。这些是我考虑过的选择:

  • 在我域的应用程序服务部分,我使用聚合,调用它的方法,并尝试将结果序列化到DTO。为了将其序列化为DTO,我必须使用
    instanceof
    检查当前节点是复合节点还是子节点,然后继续序列化。由于
    instanceof
    是代码气味(正如我读到它打破了打开/关闭原则等),我决定尝试使用访问者模式

  • 为了应用Visitor模式,我的复合聚合必须实现Visitor,它将返回DTO,然后DTO成为域层的一部分——这也是不好的设计(因为域应该只包含域概念,而DTO不是域层的一部分)。DTO序列化是唯一不应该进入域层的技术细节

  • 有没有其他解决方案不与这些设计原则相冲突


    有没有一种方法可以在java中模拟重载方法的动态绑定(除了
    instanceof
    ——因为这可以解决我在选项1中的问题)?

    如果访问者有一个泛型返回类型,那么访问的类就不会耦合到该类型

    public interface Node {
        <T> T accept(NodeVisitor<T> visitor);
    }
    
    public class ANode implements Node {
        @Override
        public <T> T accept(NodeVisitor<T> visitor) {
            return visitor.visit(this);
        }
    }
    
    public class BNode implements Node {
        @Override
        public <T> T accept(NodeVisitor<T> visitor) {
            return visitor.visit(this);
        }
    }
    
    public interface NodeVisitor<T> {
        T visit(ANode aNode);
        T visit(BNode bNode);
    }
    
    public class DtoNodeVisitor implements NodeVisitor<DTO> {
        @Override
        public DTO visit(ANode aNode) {
            return new DTO(); //use ANode to build this.
        }
        @Override
        public DTO visit(BNode bNode) {
            return new DTO(); //use BNode to build.
        }
    }
    
    公共接口节点{
    不接受(不接受访客);
    }
    公共类节点{
    @凌驾
    公众不接受(不接受访客){
    回访者。参观(本);
    }
    }
    公共类BNode实现节点{
    @凌驾
    公众不接受(不接受访客){
    回访者。参观(本);
    }
    }
    公共接口节点检测器{
    T参观(阳极);
    T访问(BNode BNode);
    }
    公共类DtoNodeVisitor实现NodeVisitor{
    @凌驾
    公众DTO参观(阳极){
    return new DTO();//使用ANode来构建它。
    }
    @凌驾
    公众DTO访问(BNode BNode){
    返回新的DTO();//使用BNode生成。
    }
    }
    

    阳极
    BNode
    不知道这里的
    DTO

    首先,在第2点,我不知道:

    我的复合聚合必须实现访问者

    我想到的第一个问题是,为什么? 您不能将访问者声明为接口,并将实现作为聚合的输入参数传递吗

    有没有一种方法可以在java中模拟重载方法的动态绑定(instanceof除外,因为这可以解决选项1的问题)

    是的,你可以用反射来做,但真正的问题是,你想用它们吗

    我认为答案取决于你必须处理多少案例,以及它们改变的频率

    如果您有“可管理”数量的不同案例,那么使用
    instanceof
    的解决方案可以很好地权衡:

    public Something myMethod(Entity entity){
        if (entity instanceof AnEntity){
             //do stuffs
        else if (entity instanceof AnotherEntity){
             //do something else
        ...
        else {
             throw new RuntimeException("Not managed " + entity.getClass().getName());
        }
    }
    
    否则,如果您有更多的案例,并且希望在自己的方法中拆分代码,您可以使用Java
    MethodHandle
    ,让我发布一个用法示例:

    package virtualmethods;
    
    import java.lang.invoke.MethodHandles;
    import java.lang.invoke.MethodType;
    
    public class MyObject {
    
        public String doStuffs(Object i) throws Throwable {
            try {
                final MethodType type = MethodType.methodType(String.class, i.getClass());
                return (String) MethodHandles.lookup()
                    .findVirtual(getClass(), "doStuffs", type)
                    .invoke(this, i);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Not managed " + i.getClass().getName(), e);
            }
        }
    
        private String doStuffs(Integer i) {
            return "You choose " + i;
        }
        private String doStuffs(Boolean b) {
            return "You choose boolean " + b;
        }
    }
    
    然后使用它:

    package virtualmethods;
    
    public class Main {
    
        public static void main(String[] args) throws Throwable {
    
            MyObject object = new MyObject();
    
            System.out.println("Integer => " + object.doStuffs(5));
            System.out.println("Boolean => " + object.doStuffs(true));
            try {
                System.out.println("String => " + object.doStuffs("something"));
            }
            catch (Throwable e) {
                System.out.println("KABOOM");
                e.printStackTrace();
            }
        }
    
    }
    
    MyObject
    中的public方法获取一个
    对象
    将查找名为
    dostufs
    的方法,该方法具有
    字符串
    结果和
    i.getClass()
    作为类
    MyObject
    中的输入(详细信息)。
    使用这种方式,您可以在运行时分派方法(在编译时使用重载是静态链接)。 但这两种方法都有一个问题,即您无法确定在第一种情况下是否会管理扩展/实现
    实体的所有类型,在第二种情况下,和/或
    对象的所有类型,这两种解决方案都有一个
    else
    catch
    来检查何时将未管理的类型传递给该方法。
    100%确保您正在管理所有类型,这只能通过@jaco0646提出的解决方案来实现,因为据我所知,它会强制您管理所有类型,否则它将无法编译。
    考虑到它所需要的样板文件数量,我只会在抛出会导致业务问题的
    RuntimeException时使用,我不能保证它不会使用适当的测试抛出(除此之外,我发现它非常有趣)。

    听起来你在过度复杂化它。如果您需要
    typeof
    ,那么您的聚合没有返回有效的域对象。它返回的域对象太通用。为了解决这个问题,您可以将聚合方法分为两种方法;一个返回Child,另一个返回Composite。然后,您的应用程序服务决定调用哪一个(如果可能)

    如果出于某种原因,您需要使用聚合返回泛型对象,我会重新考虑您选择的设计


    另一个“黑客”是简单地在域对象上放置一个属性,指示它是复合对象还是子对象。我假设聚合将知道它是否是,并且能够准确地填充该属性。

    AFAIK有“统一设计”和“类型安全设计”(Design for type safety)()。你说的是第二个,而我的实现是第一个。我唯一需要区分Composite和Child的时候是在进行序列化/反序列化时,但我想应该是这样的。有些情况下不需要DDD,这可能是其中之一。当您进行DDD时,您是为“SME”而设计的,而不是专门为类型安全而设计的(即使最终可能是这样)。话虽如此,我在过去所做的是让父节点和叶节点实现一个INode接口,该接口具有一个包含您要查找的内容的type属性。关于访问者实现,我的意思是域必须了解DTO,因为要序列化