I'm implementing a program within Swing, and I've read Nirmal's implementation of this pattern in Swing, which seems to show a rather elegant handling of the whole "separation of responsibilities" concept.
However, as I'm developing a more complicated program than the one posted by Nirml, which consists of a single JFrame container, I look for guidance as how to implement MVC properly.
My program will be consisting of sub-containers and such. I am curious as to how the Controller is supposed to implement the logic behind defining and assigining all the listeners of the View.. or if the controller defining the listeners for every single View component is even practical?
It would seem that I would need a method in the View's top-level container to allow the Controller to invoke the view to add a Listener to the component in question? and so I would need a chain of methods, each passing down the listener from the top-level container, to the immediate container holding the component.. culminating with the container invoking addActionListener() on it.
Is this the proper way to handle listeners in MVC?
Is defining all listeners of every component in the View by the Controller mandatory in MVC, or a useful practice? This would also imply that I create methods in the top-level container(View) to give the Controller a way to assign listeners to every single component in sub-containers?
Okay, first things first, Swing implements a form of the MVC already, albeit in the form of VC-M. This means that you shouldn't try and constrain Swing to a pure MVC directly, as you'll be very disappointed and spend a lot of time trying to make hacks where they shouldn't be.
Instead, you can wrap a MVC around Swing, allowing it to work around the API instead.
To my mind, a controller doesn't need to know, nor should it care, how the view or model are implemented, but it should only care how it can work with them (I've had too many developers get hold of UI components and do things to/with them that they shouldn't have, and broken the API when we've changed the implementation. It's best to hide that kind of detail)
In this vein, you can think of a view as self contained entity - it has controls and it does stuff independent of the controller. The controller doesn't care about the implementation specifics. What it does care about is getting information and been told when some event, described by the contract, has occurred. It shouldn't care about HOW it was generated.
For example, say you have a login view. The controller only wants to know the user name and password that the user entered and when it should validate that information.
Let's say you implement the view/controller to expose the
JTextField
andJPasswordField
s to start with, but later on, your users want the user name selection to be restricted to a specific list (possibly provided by the model). Now you have implementation details stuck in your controller which are no longer applicable and you have to manually change or create a new MVC for this new use case.What if, instead, you simply stated that the view has a getter for the user name and password and some kind event listener which would tell the controller when the user wanted the credentials verified? Well now, you'd only need to provide a new view, no need to modify the controller. The controller won't care HOW these values are generated.
As to the greater aspect of your question.
The general answer is, no, it's not the proper way.
Each sub view would become its own MVC, with it focusing on its own requirements. The parent MVC might use events or other functionality provided by the child MVC's to make updates or even modify the states of other child MVCs.
The important thing to remember here, is a view can act as a controller for other views, although, you might choose to have a series of controllers which the view would be allowed to manage.
Imagine something like a "wizard". It has a bunch of steps which collects various information from the user, each step needs to be valid before it can move on to the next step.
Now, you might be tempted to integrate navigation into this directly, but a better idea would be to separate the navigation details as its own MVC.
The wizard would, when asked, present a step to the user, the user would fill in the information, possibly triggering events. These events would then allow the navigation MVC to decide if the user can move to the next step or the previous step.
The two MVC's would be controlled by a third "master" MVC which would help manage the states (listening for events from the wizard and updating the state of the navigation)
Let's try an example with a question that gets asked way to much around here, a quiz!
A quiz has questions, each question has a prompt, a correct answer, a series of possible answers and we also want to store the resulting answer from the user.
The Quiz API
So, below we have the basic outline of the quiz MVC, we have a question, which is managed by a model, there is a controller and a view and series of observers (listeners)
The Contracts (interfaces)
I, personally, work to the principle of "code to interface (not implementation)", I've also deliberately gone overboard with the idea to demonstrate the point.
If you look closely, you will note that neither the view or model actually have any relationship to each other. This is all controlled via, the controller
One of the things I've done here is to provide the controller with a
askNextQuestion
, because the controller doesn't know when that should occur (you might think about using theuserDidChangeAnswer
, but that would mean that the user only gets a single attempt to answer the question, kind of mean)The implementation
Now, normally, I like to have some
abstract
implementations laying around to fill out the "common" functionality, I've forgone that for the most part and gone directly to the default implementation, this done mostly for demonstration purposes.Really nothing fancy here. About the only thing of significant interest here is how the controller is managing the events between the model and the view
The Navigation API
The navigation API is pretty basic. It allows you to control if the user can actually navigate to the next or previous element (if the actions should be made available to the user) as well as disable either of the actions at any time
(Again, I've focused on a simple design, realistically, it would be nice to have some control over modifying the state of the model to change in which directions the navigation can work, but I've left this out on purpose to keep it simple)
The Contracts (interfaces)
The implementation
The Quiz Master
Now, these are two distinct APIs, they have nothing in common, so, we need some kind of controller to bridge them
The contracts (the interfaces)
Okay, so you're probably asking yourself the obvious question, where's the model? Well, it doesn't need one, it's just a bridge between the navigation and quiz APIs, it doesn't manage any data of it's own...
The implementations
Now, the implementation is interesting, it actually has two "states" or "views", the "question and answer" view and the "score view". This, again, is deliberate, because I really didn't want ANOTHER MVC. The Q&A view is already managing two MVCs any way :P
Basically, what this does is monitors the quiz API for when the user changes the answer to a question, it then tells the navigation API that it can move to the next question. It monitors the start and completed events as well, presenting the required view for those states.
It is also monitoring the navigation API for navigation events. In this example, we can only move in a single direction and even if the navigation API was configured to do otherwise, the quiz API does not provide that functionality
Put it together
Now, I've chosen to deliberately build each section separately, conceivably, you could have the
QuizMasterController
build theNavigation
API itself, as it knows that the quiz API only allows for forward navigation, equally we could change the navigation API to allow those states to be modified via the model or the model changed, these are all viable solutions, I've just gone for a direct example.And finally we end up with something like...
This is nothing if not rough, but is designed to provide some ideas into how you might accomplish complex, compound MVCs