ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

java – 用于层间通信的装饰器

2019-06-08 20:51:55  阅读:160  来源: 互联网

标签:java design-patterns architecture domain-driven-design


我试图通过使用抽象模型和装饰器来实现用于层间通信的新架构方法.

传统上,当我们设计单片应用程序的层时,我们将拥有应用程序(控制器)层,域(业务)层和基础架构(持久性)层.这些层通过具体的模型定义相互通信,当从数据库中读取时,导航通过存储库,然后导航到服务,最后导航到将其分派给客户端的控制器.

那就是说,让我们谈谈……

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

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

所以,这就是我要做的事情:通过使用装饰器进行通信来分离图层.

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

例如.:

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

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

>在内存中只有1个对象包含Foo信息,但每个层都有
“介意它自己的事业”.
>将图层分隔在不同的模块中,因此业务仅导入infra,而app层仅导入业务.
>我们可以分发用于哑逻辑的存根而不会泄露我们的存根
域,就像购物车服务一样,只能知道摘要
一个产品,但不是域对象本身.
>我们可以让不同的团队在应用程序的不同层中工作而不会发生冲突.

这是一个纸上的例子:

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

解决方法:

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

我持怀疑态度.称它为两个9s.

在架构上,你的草图表明这些孤立的组件是同行的,我认为这根本不是建立关注点的实用方法.域对业务非常重要.持久性,而不是那么多(如果我们有足够的内存,并且可以保持我们的服务器运行,我们就不会打扰).

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

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

最重要的是,我认为所绘制的架构使得依赖关系的真正复杂性变得微不足道.认识到这些是具有不同关注点的不同组件的部分原因在于您可以将它们彼此交换.每次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有同样的问题);你只在FooBarView中包装FooDTO,因为你要将它交给序列化层.

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

那篇文章关注注释,做魔术,但实际上并不关心FooDTO表面.这种实现也同样有效

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

    // ...
}   

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

标签:java,design-patterns,architecture,domain-driven-design
来源: https://codeday.me/bug/20190608/1200068.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有