【问题标题】:How can this circular, bidirectional dependency be resolved?如何解决这种循环的、双向的依赖?
【发布时间】:2011-03-22 09:11:02
【问题描述】:

我有一个 RequestHandler 类和一个 RequestListener 类。 RequestHandler 创建一个 RequestListener 并将一个引用传递给它自己。 RequestListener 依次调用 RequestHandler 上的方法来处理不同类型的请求(例如,handleTypeARequest()、handleTypeBRequest() 等)。不幸的是,RequestHandler 也调用了 RequestListener 上的方法(例如 processNextRequest()),所以我有一个循环依赖:

class RequestHandler {
   RequestListener requestListener;
   ...
}

class RequestListener {
   RequestHandler requestHandler;
   ...
}

这意味着两者之间的耦合更紧密,通常被认为是代码异味。

一种解决方案是使用不同的对象来封装每个请求,而不是使用不同的方法。当出现提示时,RequestListener 可以处理请求并为其返回某种类型的 Request 对象。不幸的是,我不太喜欢这种方法,部分原因是增加了更多对象和类的复杂性,部分原因是性能问题(这很重要);直接在 RequestHandler 上调用 handleXXXRequest() 方法比创建一堆对象并可能还维护一个堆栈来缓冲它们直到需要时要快得多。

这个问题还有其他解决方案吗,还有,真的是个问题吗?

【问题讨论】:

  • “请求监听器”是否应该负责处理请求?在我看来,听者只负责听。 “请求处理程序”应该负责处理或“处理”听众听到的内容。

标签: language-agnostic circular-dependency circular-reference


【解决方案1】:

是的,这真的是个问题吗?

就像你说的那样,父 子引用存在问题,两者都相互引用。我不相信这里真的有问题。

【讨论】:

    【解决方案2】:

    您的编程语言最有可能允许您转发声明类,从而让您越过语法错误部分。

    如果是 C++,我会这样做:

    class RequestListener;
    
    class RequestHandler {
        RequestListener *requestListener;
        /* ... */
    }
    
    class RequestListener {
        RequestHandler *requestHandler;
        /* ... */
    }
    

    但是,请注意,如果您尝试以递归方式嵌套对象本身,则会出现问题(因为您会得到一个无限大的结构):

    class RequestListener;
    
    class RequestHandler {
        RequestListener requestListener;
            // the compiler will complain about an incomplete type here
        /* ... */
    }
    
    class RequestListener {
        RequestHandler requestHandler;
        /* ... */
    }
    

    既然你只想让对象相互引用而不是相互包含,你应该没问题。

    【讨论】:

    • 对不起,我没说清楚,问题是紧耦合以及如何最小化它,而不是语法错误。它现在编译(和工作)很好。
    【解决方案3】:

    事件允许您通知其他对象某些状态更改,而无需显式引用类。

    class RequestListener
    {
        public event EventHandler<RequestReceivedEventArgs> RequestReceived;
    
        public void ProcessNextRequest(object sender, RequestHandledEventArgs e)
        {
            // Process next request.
        }
    }
    
    class RequestDispatcher
    {
        public event EventHandler<RequestHandledEventArgs> RequestHandled;
    
        public void DispatchRequest(object sender, RequestReceivedEventArgs e)
        {
            // Invoke correct RequestHandler class/method.
    
            // Raise RequestHandled event when request handler has finished.
        }
    }
    
    var listener = new RequestListener();
    var dispatcher = new RequestDispatcher();
    
    // Subscribe to each other's events.
    listener.RequestReceived += dispatcher.DispatchRequest;
    dispatcher.RequestHandled += listener.ProcessNextRequest;
    

    上面的 C# 示例遵循 .NET Framework Guidelines,因此非常冗长,但它应该说明类之间的解耦。我还引入了一个 RequestDispatcher 类,它负责调用正确的处理程序,而不是让侦听器处理这个问题。

    如果不想创建额外的类,您可以将此模型剥离为您真正需要的东西。例如,您可以在侦听器中公开TypeARequestReceivedTypeBRequestReceived 事件。然后您的请求处理程序方法可以直接订阅这些事件。

    【讨论】:

    • 我不确定您的“调度程序”和 OP 的“处理程序”是否具有相同的目的。
    • 不,他们没有。在 OP 的示例中,侦听器负责调用正确的处理程序。我已将此逻辑提取到调度程序中。调度程序负责将请求委托给 OP 的处理程序之一。调度程序上的 RequestHandled 事件可能不合适,但我想使示例尽可能简短。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-23
    • 2012-03-15
    • 2016-09-21
    相关资源
    最近更新 更多