Konrad Garus
Konrad Garus

Reputation: 54035

Circular dependency between "controller" and "gui"

I am writing a complex GUI in Java with many components on several screens working on top of and interacting with a shared piece of logic and model. Clearly there are some circular dependencies between the "gui" and "controller/logic": User actions in GUI are forwarded to controller, which performs some task and then needs to reflect those changes in all GUIs. Something may happen in background that makes controller push updates to GUIs. And so on.

Now, here's my question. Listener or observer pattern works great for pushing updates to GUI. Is it OK to make my GUI depend on a concrete controller class directly? Why/why not? There is (and is always going to be) only one such controller. There is like a dozen controller calls that the GUIs need for polling state and executing actions - I am not in love with the idea of a handful of trivial callback interfaces that are always going to only have one implementation, nor one giant callback interface for all kinds actions.

Upvotes: 7

Views: 2392

Answers (3)

Heisenbug
Heisenbug

Reputation: 39194

I completely agree with @Charles when he says that:

loose coupling is fundamental to good design.

the reasons are clear and he already explained them. I'd like just to share with you a pattern slightly different from the MVC, where the view and the controller are the same entity. It's called Model-Delegate and it's used by Swing. The linked article describe it deeply.

Upvotes: 1

ak3nat0n
ak3nat0n

Reputation: 6288

I am not an expert in everything MVC/MVP but here is what I do most of the time.

For simple applications where UI automation and testing is not an issue, I go with the GUI depending on a concrete controller (My view actually instantiates the controller). What I do though is make sure the GUI implements some view interface that the controller takes as a parameter in its constructor. In that way, I don't need to use any event or observer-like behavior: the controller has access to the view interface and the view holds a controller as its member. The problem with this approach in my opinion is that it is not really test friendly. While some may argue that MVP (Model View Presenter) may be based on this approach, I don't like the fact that the controller cannot exist without a GUI object being instantiated. Even if you pass a controller as a parameter of the GUI object in its constructor, I tend to consider it more like a hack. Another issue with that approach is that the GUI actually is not passive enough. I like the logic in my UI to be handled by the controller, not the other way around . But that may just be a personal choice.

For more advanced applications where testing becomes a big issue, I make sure the GUI does not even know about a controller since the controller is the one that instantiates the GUI object (either directly or using dependency injection). Something in the line of a supervising controller. This approach helps a lot with testing because I can actually mock the whole UI by providing a fake implementation of the GUI or even reuse my controller with a different implementation of the view. The UI actually in this case can raise events that the controller would handle but only the controller would modify the underlying model or push modifications to the View if needed (as opposed to the View "pulling" that data from the controller to update itself.)

Just my 2 cents...

Upvotes: 0

Igby Largeman
Igby Largeman

Reputation: 16747

Is it OK to make my GUI depend on a concrete controller class directly? Why/why not?

No: Because loose coupling is fundamental to good design. Depend on abstractions. Depending on implementations makes it impossible to substitute one implementation for another without recompiling the dependent at the very least. Tightly coupling any two classes impedes future flexibility.

Yes: It's a judgement call. If loose coupling definitely has no future benefit and is too expensive to justify, couple away.

But really try not to. Having the GUI depend on the controller is a valid design decision. But always depend on abstractions, never implementations. Come on... you knew that. I mean, okay, if nobody ever wants to switch out the controller, then you haven't gained anything by using an interface. Except peace of mind, knowing you wrote tidier, less-coupled code. And arguably easier to test.

As for the choice of how the communication between GUI and controller occur, it's a toss-up. You could use events, but wiring them up is a drag and they won't cross app boundaries. But by using events you may feel you have the loosest possible coupling. GUI and controller never need to address (abstractions of) each other except to initially wire up the events. That might feel nice. Or you could use method calls against interfaces, which may feel like a slightly tighter coupling, but it's hardly different really. Any way you do it, X has to know things about Y for them to communicate.

Circular dependencies between GUI and controller are okay (on abstractions!). There are many variations of the MVC pattern. Every time I use it, I mold it to my needs/mood. That said, I do try to avoid circular dependencies. I prefer to limit dependencies to one direction. Or none at all!

For example in my current project the controllers know about the views, but the views have absolutely no idea that the controllers exit. The controllers susbcribe to the views' events, and pass data back to them via a single state object which the view binds to. The state class is the only thing the view depends on. And the only thing the controller knows about the view is it's interface and the type of its state object. And those things are defined in an external module so that the view module can be removed completely and the controller will still compile, and vice-versa. This is a very loose coupling. Almost none at all (both sides depend on a third module instead).

Some people do it the other way 'round.

Reasons to avoid concrete dependencies

  • difficult to swap one implementaton for another - dependent will likely need modification or at least recompilation
  • harder to maintain - can't modify components independently of each other
  • harder to test in isolation - need to modify and recompile dependent to swap in a mock implementation of dependency

Upvotes: 7

Related Questions