有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

用于层间通信的java装饰器

我正试图通过使用抽象模型和装饰器来实现层间通信的新体系结构方法

传统上,当我们设计单片应用程序的层时,我们将有一个应用程序(控制器)层、一个域(业务)层和一个基础结构(持久性)层。这些层通过一个具体的模型定义相互通信,当从数据库中读取时,该模型定义将在存储库中导航,然后导航到服务,最后导航到控制器,控制器将其发送到客户机

也就是说,让我们开门见山

通常,基础架构模型不得与业务模型相同,业务模型也不得与UI模型相同。 基础架构模型可能只关注其目标存储库结构,域层可能只关注业务操作,UI层必须只关注其客户端和数据读取/输入

所有这些层都必须“理解”彼此的抽象,甚至实现双向值映射(或DTO),以便在它们之间进行通信。Mapping/DTO是一种糟糕的方法,因为我们将对数据映射进行不必要的处理

所以,我想做的是:通过使用装饰器进行通信来分离层

在这种方法中,我们将在一个共享模块上有抽象组件和它的装饰器,并且每一层都有它自己的装饰器

例如:

  • AbstractFoo是一种抽象
  • FoodDecorator实现AbstractFoo并包装AbstractFoo
  • FooEntity是一个基础架构模型,它实现了FooDecorator并处理持久性内容
  • FooBusiness是一个实现FooEntity装饰器并处理业务内容的业务模型
  • FooView是一个控制器模型,它实现FooBusinessDecorator并处理UI字段、编码等

通过这种方法,我们可以:

  • 内存中只有一个对象保存Foo信息,但每个层 “管好自己的事”
  • 在不同的模块中分离层,因此业务只导入infra,而应用层只导入业务
  • 我们可以分发愚蠢逻辑的存根,而不必泄露我们的信息 域,如购物车服务,必须只知道摘要 一个产品,但不是域对象本身
  • 我们可以让不同的团队在应用程序的不同层中工作,而不会产生冲突

下面是一个纸面上的例子:

Sketch

所以。。。如果这是一个好方法,我需要的是建议、技巧和一些意见

[更新#1]:

为了简化我的问题,我将在下面发布一个用例:

共享组件:

// Abstract Foo
public abstract class AbstractFoo {
   protected Long id;
   protected String color;
   protected LocalDateTime lastModified;

   protected Long getId();
   protected String getColor();

   protected void setId();
   protected void setColor();
}


// Abstract Decorator(wraps foo)
public abstract class FooDecorator extends AbstractFoo {
   private final Foo foo;
   protected FooDecorator(Foo foo) { this.foo = foo; }

   protected Long getId() {return foo.getId();}
   protected String getColor() {return foo.getColor();}
   protected LocalDateTime getLastModified() {return foo.getLastModified();}

   protected void setId(Long id){foo.setId(id);}
   protected void setColor(String color){foo.setColor(color);}
   protected void setLastModified(LocalDateTime lastModified){foo.setLastModified(lastModified);}
}

基础架构层:

// Defines the database model for Foo
// Only concerned about the table structure
@Entity
@Table(name="TBL_FOO")
public class FooEntity extends FooDecorator {
   public FooEntity(Foo foo) {
      super(foo);
   }

   @Id @AutoGenerated @Column(name="ID_FOO")
   protected Long getId() {
      return super.getId();
   }

   @Column(name="DS_COLOR", length="255")
   protected String getColor(){
      return super.getColor();
   }

   @Temporal @Column(name="DT_MODIFIED")
   protected LocalDateTime getLastModified(){
      return super.getLastModified();
   }

}

域(业务)层

public class FooBar extends FooEntity {

   public FooBar(Foo foo) {
      super(foo);
   }

   //let's open the ID for the outside world
   public Long getId() {
      return super.getId();
   }

   // Paint with a red color
   public void paintAs(Color color) {
      super.setColor(color.getKey());
   }

   // Upper level may want to know the current color
   public Color getColor() {
      return Color.parse(super.getColor());
   }

   public boolean isModifiedSince(LocalDateTime compare) {
      return DateUtils.compareMillis(super.getLastModified(), compare) > 0;
   }

   public LocalDateTime getLastModified() {
      return super.getLastModified();
   }

}

应用程序(视图)层

/**
 * JSON Eg.:
 * {fooBarId: 1, fooBarColor: 'RED'}
 */
public class FooBarView extends FooBar {
   public FooBarView(Foo foo) {
      super(foo);
   }

   // Maps field to JSON as 'fooBarId'
   @JsonMap("fooBarId");
   public Long getId() {
      return super.getId();
   }

   // Maps field to JSON as 'fooBarColor'
   @JsonMap("fooBarColor")
   public String getColor() {
      return super.getColor().toString();
   }

}

-

// Pseudo Code
public class FooBarREST {
   // '/api/v1/foobar/{id}'
   public getFooBar(Long id) {
      return new FooBarView(find(id));
   }
}

共 (1) 个答案

  1. # 1 楼答案

    So...what I ask is for suggestion, tips and some opinions if it's a good approach.

    我持怀疑态度。称之为两个9

    在架构上,你的草图表明这些孤立的组件是对等的,我认为这根本不是一种实用的建模方法。域名对企业来说非常重要。持久性,没有那么多(如果我们有足够的内存,并且可以保持服务器运行,我们就不会麻烦了)

    此外,还有一些相互渗透的设计选择;如果您选择的持久性是存储事件历史记录,那么您的域模型和存储库必须在这一点上达成一致,并且这两个组件之间的契约应该明确,但是您的其他组件都不关心如何实现某个事物的状态,它们只需要一些查询表面

    甚至可能不是这样——应用程序用表示而不是对象来响应客户机的查询;如果这些表示被提前缓存(CQR),那么它根本不关心域模型中的对象

    除此之外,我认为所绘制的体系结构淡化了依赖关系的真正复杂性。认识到这些是具有不同关注点的不同组件的部分意义在于,您可以将它们相互替换。每一次api更改都不应该是重建世界的事件(想想语义版本控制)

    添加样本代码后添加的澄清

    // Abstract Foo
    public abstract class AbstractFoo {
        protected Long id;
        protected String color;
        //...
    }
    
    // Abstract Decorator(wraps foo)
    public abstract class FooDecorator extends AbstractFoo {
        // ...
    }
    

    这就是破绽——为什么你想让每个装饰师都有自己的状态副本

    我认为,部分问题在于你把Decorator模式和Adapter模式搞混了

    public interface Foo {
        Long getId();
        Color getColor();
        LocalDateTime getLastModified();
    }
    
    public interface FooDTO {
        Long getId();
        String getColor();
        LocalDateTime getLastMofified();
    }
    

    这些是适配器:

    public class FooDTOAdapter implements FooDTO {
        private final Foo foo;
    
        // ...
        String getColor() {
            return foo.getColor().toString();
        }
    }
    
    public class FooAdapter implements Foo {
        private final FooDTO dto;
    
        // ...
        Color getColor() {
            return Color.parse(dto.getColor());
        }
    }
    

    他们是装饰师尽管他们不是很好的装饰师,见下文

    public class FooBarView implements FooDTO {
        private final FooDTO dto;
    
        //...
    
        // Maps field to JSON as 'fooBarColor'
        @JsonMap("fooBarColor")
        public String getColor() {
           return dto.getColor();
        }
    }
    
    @Entity
    @Table(name="TBL_FOO")
    public class FooEntity implements FooDTO {
        private final FooDTO dto;
    
        // ...
    
        @Column(name="DS_COLOR", length="255")
        public String getColor(){
            return super.getColor();
        }
    }
    

    我认为这种方法越来越接近你想要的。例如,模型使用的存储库的签名如下所示:

    interface FooRepository {
        save(Foo foo);
    }            
    

    将模型连接到实体存储的实现类似于

    class Connector implements FooRepository {
        private final Store<FooEntity> entityStore;
    
        //...
    
        void save(Foo foo) {
            FooDTO dto = new FooDTOAdapter(foo);
            FooEntity entity = new FooEntity(dto);
            entityStore.save(dto);
        }
    }
    

    好消息是,每个组件都可以通过其首选镜头查看状态,而实际上不需要复制任何数据

    不过,您应该知道,每次数据通过一个层时,珍珠都会变大,因为接口会被交换以适应新组件。这本身并不好也不坏,这只是一件需要注意的事情;因为您选择不在接口处复制数据,所以它离您越来越远

    FooBarView是作为上面的装饰器实现的;这不是一个好例子,因为这不是实现所扮演的角色(FooEntity也有同样的问题);只在FoodBarView中包装FooDTO,因为要将其交给序列化层

    class FooBarViewWriter {
        void writeTo(JsonWriter json, FooBarView view) {
            // ...
        }
    }
    

    这篇文章关注的是注释,发挥了神奇的作用,但它实际上并不关心食物到表面的情况。这种实现也同样有效

    public class FooBarView /* Not a FooDTO */ {
        private final FooDTO dto;
    
        //...
    
        // Maps field to JSON as 'fooBarColor'
        @JsonMap("fooBarColor")
        public String getColor() {
           return dto.getColor();
        }
    }
    

    换句话说,它只是一个适配器,意外地写入了Decorator模式。您会得到一些编译时检查,表明您已经实现了所有的签名,但没有太多其他功能

    更可能的装饰器是向实现添加方面的装饰器

    public class TimedFooRepository implements FooRepository {
        private final FooRepository repo; 
    
        public void save(Foo foo) {
            Timer timer = start();
            try {
                repo.save(foo);
            } finally {
                stop(timer);
            }
        }
    
        // ...
    }   
    

    抽象装饰程序通常在多个实现只向内层发送调用时出现。与其一遍又一遍地编写代码,不如只编写一次,然后让具体的实现选择需要替换默认行为的地方

    abstract class AbstractFooRepository implements FooRepository {
        private final FooRepository repo;
    
        protected AbstractFooRepository(FooRepository repo) {
            this.repo = repo;
        }
    
        public void save(Foo foo) {
            repo.save(foo);
        }
    
        // ...
    }
    
    public class TimedFooRepository extends AbstractFooRepository {
        // No longer necessary to keep our own handle
        /* private final FooRepository repo; */
    
        public TimedFooRepository(FooRepository repo, ...) {
            super(repo);
            // ...
        }
    
        public void save(Foo foo) {
            Timer timer = start();
            try {
                super.save(foo);
            } finally {
                stop(timer);
            }
        }
    
        // ...
    }   
    

    只有一个方法的抽象装饰器是非常愚蠢的,因为每个实现都将重写该方法。但它说明了这个想法