views:

64

answers:

3

I have a RequestHandler 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?

+2  A: 

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.

gruszczy
+1  A: 

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.

Joey Adams
Sorry, I wasn't clear, the problem is tight coupling and how to minimize it, not syntax errors. It compiles (and works) fine at the moment.
james
I edited the question to clarify
james
A: 

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.

Niels van der Rest
I'm not sure your 'dispatcher' and OP's 'handler' have the same purpose.
izb
No, they don't. In OP's example the listener was responsible for calling the correct handler. I've extracted this logic into the dispatcher. The dispatcher is responsible for delegating the request to one of OP's handlers. The `RequestHandled` event on the dispatcher is probably out of place, but I wanted to keep the example as short as possible.
Niels van der Rest