【问题标题】:Presentation of an Aggregate Member聚合成员的介绍
【发布时间】:2018-05-10 11:38:51
【问题描述】:

您如何公开聚合成员以进行演示,同时防止该成员被直接修改?因此,我需要对该成员的只读访问权限,以便在 UI 上显示它,例如:

class A {

    B b;

    void doSomething() {
        b.update();
    }

}

class B {

    String getTitle() {
        return title;
    }

    Items getItems() {
        return items;
    }

    void update() {
        ...
    }

}

interface SomeView {

    void show(Items items);

}

快速而简单的解决方案是添加一个方法A::getB() 并调用someView.show(b.getItems()),但是返回的B 实例可以在A 之外进行修改(B::update() 可以在@ 之外调用987654327@).

我正在考虑几种解决方案,但不太确定哪些是最好的,或者是否已经有这样做的常见做法。

我想到的一个解决方案是使用A 返回的只读版本的B

class A {

    ReadOnlyB getReadOnlyB() {
        return new ReadOnlyB(b);
    }

}

class ReadOnlyB {

    B b;

    ReadOnlyB(B b) {
        this.b = b;
    }

    String getTitle() {
        return b.getTitle();
    }

    Items getItems() {
        return b.getItems();
    }

}

【问题讨论】:

  • 您的应用程序中有层吗?如果是这样,它们的依赖模式是什么?您如何将它们彼此隔离?
  • 我有领域层、API 层和表示层。实际上对于视图,我会使用ItemPresentation 而不仅仅是Item 并让ItemMapperItem 映射到ItemPresentation
  • 不管怎样,即使是同层的实体,A::getB()也不是什么好东西。就像如果有一个单独的类C 也属于域层,那么C 能够直接访问B 仍然不好。

标签: oop architecture domain-driven-design


【解决方案1】:

不要将域对象暴露给表示层,将它们映射到只读表示模型(根据您的评论,这已经是您确定的下一步要做的事情)。

您可以查看 CQRS,了解更严格、更有主见的读取模型。

无论如何,即使对于同一层的实体,A::getB() 仍然会 不是一件好事。

如果你留在域层内,它实际上是合法的。实体可以具有对其他实体的引用——包括对来自其他聚合的实体的临时引用。但是,如果 A 和 B 属于不同的聚合,您必须意识到它违反了聚合作为一致性边界方法,因为您现在很容易在同一个事务中修改多个聚合。通过增加同时访问 B 的机会,您的生活也变得更加艰难。

【讨论】:

  • If you stay inside the domain layer, it's actually legit. 嗯,这很有趣。无论如何,我认为这是一个单独的问题。将看看我如何使用 CQRS 做到这一点。谢谢。
【解决方案2】:

您也可以让对象呈现自己,在这种情况下,绝对不需要暴露隐藏的结构。这将更符合面向对象、德米特法则等。像这样:

public class A {
    private B b;
    ...
    public void paint(Graphics g) {
        ...
        b.paint(g);
        ...
    }
}

或者,使用 Wicket 类型的 Web 演示:

public class A {
    private B b;
    ...
    public Component display() {
        return new APanel(..., b.display(), ...);
    }
}

【讨论】:

  • 是的,我可能不会那样做,因为域本身与图形无关。
  • 我的意思是让像 Employee 类这样的东西有一个paint方法是没有意义的。
  • @PabloEspantoso,你错过了重点。这篇文章不是关于图形,而是更多关于 OOP。 Paint 只是一个示例,在您的情况下,方法可以称为 convert 之类的东西。
  • @JanMuncinsky 我认为在域实体上使用 convert 也没有意义,因为这意味着域实体需要知道它应该转换为的类型(在这种情况下其上层的数据类型,即:表示层)。
【解决方案3】:

您可以对B 使用只读接口。在内部,在A 类中,您使用B 类,但在外部(对客户端),您提供B 的只读接口。这是一个示例:

class A 
{
    private B b;

    void doSomething() {
        //internally you use this.b
        b.update();
    }

    //please name this method according to its purpose
    ReadOnlyB getB()
    {
       return b;
    }
}

class B implements ReadOnlyB
{
    String getTitle() {
        return title;
    }

    Items getItems() {
        return items;
    }

    void update() {
        ...
    }

}

//please name this interface according to its purpose!
interface ReadOnlyB
{
    Items getItems();
}

interface SomeView 
{
    void show(Items items);
}

但是,这会破坏Law of Demeter。最好从A返回Items,像这样:

class A 
{
    private B b;

    void doSomething() {
        b.update();
    }

    Items getItems()
    {
       return b.getItems();
    }
}

【讨论】:

  • 是的,和我最后选择的方案很接近。你也提到了A::getItems 的好点。
  • 顺便说一句,关于得墨忒耳法则,由于 ReadOnlyB 现在本质上只是一个值对象/数据结构,我不认为我们违反了法律。如果我们暴露一个实体/对象(带有行为和东西),那将违反得墨忒耳定律。
  • @PabloEspantoso 如果你这样做a.getReadOnlyB().getItems() 你违反了法律
  • 以下内容如何:Triangle::getReadOnlyPoints().get(0).getX()?还是应该是Triangle::getFirstPointX()
  • 这里引用 Clean Code 的一句话:“这是否违反 Demeter 取决于 ctxt、Options 和 ScratchDir 是对象还是数据结构。如果它们是对象,那么它们的内部结构应该隐藏而不是暴露,因此知道它们的内部结构显然违反了Demeter法则。另一方面,如果ctxt,Options和ScratchDir只是没有行为的数据结构,那么它们自然会暴露其内部结构,所以得墨忒耳不适用。”就我而言,getItems 还返回数据结构(只读,例如:ReadOnlyItems)。
猜你喜欢
  • 1970-01-01
  • 2013-06-10
  • 2012-07-07
  • 2016-08-25
  • 1970-01-01
  • 2014-03-12
  • 2013-05-06
  • 2019-04-18
  • 1970-01-01
相关资源
最近更新 更多