开发者

How can this circular, bidirectional dependency be resolved?

开发者 https://www.devze.com 2023-01-09 01:46 出处:网络
I have a RequestHand开发者_开发知识库ler class and a RequestListener class. A RequestHandler creates a RequestListener and passes it a reference to itself. The RequestListener in turn calls methods on

I have a RequestHand开发者_开发知识库ler class and a RequestListener class. A RequestHandler creates a RequestListener and passes it a reference to itself. The RequestListener in turn calls methods on the RequestHandler to handle requests of different types when they are processed (eg handleTypeARequest(), handleTypeBRequest() and so on). Unfortunately, the RequestHandler also calls methods on the RequestListener (eg processNextRequest()), so I have a circular dependency:

class RequestHandler {
   RequestListener requestListener;
   ...
}

class RequestListener {
   RequestHandler requestHandler;
   ...
}

This means tighter coupling between the two and is generally considered to be a code smell.

One solution would be to use different objects to encapsulate each request instead of different methods. A RequestListener could, when prompted, process a request and return some type of Request object for it. Unfortunately, I don't really like this approach, in part because of the added complexity of more objects and classes and in part because of the performance issues (which matter here); calling handleXXXRequest() methods on the RequestHandler directly is much faster than creating a bunch of objects and probably also maintaining a stack to buffer them until needed.

Are there any other solutions to this problem, and also, is it really a problem?


Yeah, is this really a problem?

It is as if you said, that there is a problem with parent <-> child refernces, where both hold reference to each other. I don't believe there is really a problem here.


Your programming language most likely allows you to forward declare classes, allowing you to get past the syntax error part.

If it were C++, I would do something like this:

class RequestListener;

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

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

However, note that it would be a problem if you tried to nest the objects themselves recursively (since you would get an infinitely large structure):

class RequestListener;

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

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

Since you just want the objects to reference each other rather than contain each other, you should be fine.


Events allow you to notify other objects about certain state changes, without explicitly referring to a class.

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;

The above C# example follows the .NET Framework Guidelines and is therefore quite verbose, but it should illustrate the decoupling between the classes. I've also introduced a RequestDispatcher class that is responsible for calling the correct handler, instead of letting the listener take care of this.

You can strip this model down to only the things you really need, if don't want to create additional classes. For example, you could expose TypeARequestReceived and TypeBRequestReceived events in your listener. Then your request handler methods can subscribe directly to these events.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号