【问题标题】:Reverse dependency between layers problem层间反向依赖问题
【发布时间】:2010-09-06 12:27:42
【问题描述】:

我正在为硬件安装(水循环系统)构建控制系统。我把它设计成两层:硬件描述层和控制层。

+----------------------------------------------+
|    Control (enables manipulation of devices) |
+----------------------------------------------+
        | uses (decorates)
        v
+---------------------------------------+  (de)serialize  +------------+
|            HW Description             |<--------------->| Files / DB |
| (stores physical layout, cabling etc) |                 +------------+
+---------------------------------------+

硬件描述层包含硬件图,描述管道如何连接到热交换器和其他设备。该层的数据可根据安装进行配置,并将在运行时读取。因此,硬件描述层中的所有类都应该以一种或另一种方式序列化(通过 Hibernate/序列化到 XML 左右)。

控制层将用智能装饰硬件描述层类,因此热交换器将获得与之关联的 HeatExchangerController。

在这个系统中,控制层中的实体可能需要通过硬件描述来寻找他们最近的邻居,所以热交换控制器可能会做类似的事情

HeatExchangerController neighbour = this.getHeatExchanger().getInputPipe().getOtherSide().getHeatExchanger().getController();
neighbour.notify(highLoadAlert);

问题在于,这使得下层(硬件层)意识到它上面的智能(它的控制器)。这不仅打破了层之间的单向依赖规则,而且由于所有硬件描述类都应该是可序列化的,这使代码变得复杂,因为对更高层的所有引用都需要是可选的,声明为瞬态的等。

现在我有点难以确定通过硬件描述层提供控件是一个好主意还是坏主意,或者是否有任何替代方法可以采用。我只能想到一种方法,它涉及镜像和委托几乎所有的硬件描述层,这似乎是一种很好的侵入方式。

所以我的问题是你们中是否有人认识到这种情况并有任何提示/经验。它有名字吗?是否有任何好的模式/最佳实践,是否有任何其他知名的库/框架也已经解决了这种情况?

谢谢。

【问题讨论】:

    标签: java dependencies layer decoupling


    【解决方案1】:

    您还没有向我们展示将控制器逻辑与硬件描述分开会带来什么好处。您确定这种分离值得付出努力吗?

    听起来您正在构建Anemic Domain Model

    【讨论】:

    • 好东西。这是我一直在想的,它在某种程度上是不正确的。最大的问题是很难不将数据与逻辑分开。在简单的示例中可能是可能的,但是硬件描述和逻辑之间确实存在很大差异,需要采用易于序列化的格式。所以有人也可以争论(根据 DDD)我有两个子域,它们应该有两个不同的模型。
    • 为什么难?为什么不能简单地将控制器的方法移动到可序列化(或以其他方式持久化的类)中?如果类具有 setter 和 getter 之外的方法,我所知道的任何对象持久性框架都不会受到困扰......
    • 它通常比你描述的要复杂 - 首先你有我提出的另一个模型参数,但是对象序列化技术通常对对象提出相当高的要求,所有属性都必须是可修改的(JAXB),初始化的顺序没有定义,构造函数没有被调用等等。它可能在经典的 JPA 环境中工作,对于一个具有超过微不足道的相互依赖关系和启动序列的通用对象图,IMO 很快就会失控。
    • 最后我用瞬态字段将控制器链接到硬件描述,并在反序列化后使用装饰器(访问者)添加控制器。其他解决方案得到了详细说明,我遇到了各种其他问题。谢谢。
    【解决方案2】:

    采用领域驱动的方法。

    如果您也垂直破坏系统(域)。然后这些垂直组件可以通过控制器相互通信,仅此而已。保持每个域内的水平层,域内的分层是垂直的,并且每个域彼此隔离,因为它们只能通过控制器进行通信。

    【讨论】:

    • 你能再具体一点吗?如果我看看我的特定场景,我想不出比“调节热量”更细化的领域。你会给域和控制器起什么名字?
    • 我会说每个设备都通过控制器与另一个设备通信。
    • 对不起,我不明白如何避免硬件描述再次上升,您能详细说明一下吗?
    • 我的建议是水平分层在定义的组件内,垂直分层允许类似分层的组件之间进行通信。
    • 那么管道控制器只能与管道hw对话,而热交换器控制器只能与热交换器hw对话?似乎没有解决我的问题。
    【解决方案3】:

    不太确定您的问题是什么。如果您不想在硬件类中使用 getController() 方法,则可以将硬件->控制器映射存储在弱哈希映射中

    public class Controller
    
        static WeakHashMap<Hardware,Controller> map = ...
    
        static public Controller of(Hardware hw){ return map.get(hw); }
    
        Controller(Hardware hw)
        {
            map.put(hw, this);
        }
    

    很多时候,人们喜欢添加“方便”的方法,而不必担心依赖关系的复杂性

    user.getThis()
    user.getThat()
    user.getTheOthers()
    

    虽然User 类是一个核心类,但它不应该了解This、That 和The Others。现在很多代码都依赖于 User 类,而 User 类又依赖于很多其他的类。最终结果是应用程序中的每个类都依赖于其他所有类,一团糟。

    【讨论】:

    • 点了。但是,如果我无论如何都有这种关系,但间接地,正如你所展示的那样,会更好吗?也许我应该有这种关系。
    【解决方案4】:

    这是我想到的复杂解决方案。它在硬件层之上添加了一个装饰层,它将控制层的链接添加到每个硬件对象:

                                                                     -+
    +--------------------------+               +-----------------+    | 
    | HeatExchangerController  |               | PipeController  |     > Controller Layer
    +--------------------------+               +-----------------+    |
                ^ 1 controller                        ^ 1 controller -+
                |                                     |
                v 1 heatExchanger                     v 1 pipe       -+
    +---------------------------+  inputPipe 1 +------------------+   |
    | ControlAwareHeatExchanger |--------------| ControlAwarePipe |    > Decorated HW Layer
    +---------------------------+ 1 otherSide  +------------------+   |
                |                                     |              -+
                v 1 realExchanger                     v 1 realPipe   -+
    +---------------------------+  inputPipe 1 +------------+         |
    |      HeatExchanger        |--------------| Pipe       |          > HW Layer
    +---------------------------+ 1 otherSide  +------------+         |
                                                                     -+
    

    装饰层将包含如下代码:

    class ControlAwareHeatExchanger {
      private final HeatExchanger realExchanger;
      private final ControlAwarepipe inputPipe;
    
      public ControlAwareHeatExchanger(HeatExchanger decoratee) {
        realExchanger = decoratee;
        inputPipe = new ControlAwarePipe(decoratee.getInputPipe());
      }
    
      public ControlAwarePipe getInputPipe() {
        return inputPipe;
      }
    
      ...
    }      
    

    这样你就可以做到:

    ControlAwareHeatExchanger controlHE = heatExchangerController.getHeatExchanger();
    ControlAwarePipe controlInputPipe = controlHE.getInputPipe();
    ControlAwareHeatExchanger otherHE = controlInputPipe.getOtherSide();
    HeatExchangerController otherController = otherHE.getController();
    

    所以你可以下到装饰的硬件层,跳来跳去,然后再回到控制器层,这一切都不需要硬件层知道控制层。

    但正如您所见,您需要镜像所有连接并装饰整个硬件层来实现这一点。会涉及很多代表团。想法?

    【讨论】:

      猜你喜欢
      • 2022-01-25
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 2012-05-23
      • 2013-04-25
      • 2011-01-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多