aspose file tools*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes Decoupling the GUI Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Spring in Action this week in the Spring forum!
JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "Decoupling the GUI" Watch "Decoupling the GUI" New topic
Author

Decoupling the GUI

Christian Garcia
Ranch Hand

Joined: Jan 29, 2002
Posts: 77
In order to gain the separation of the GUI from the Controller, (after much reading at the Saloon) I came up with this solution:
From my 'AppDriver' class I create an instance of my FBNClientGUI class. Next, an instance of the Controller class that takes a 'JFrame' as it's only parameter is created. Within the Controller constructor, I cast the 'JFrame' parameter to type FBNGuiClient. Having done this, I assign ActionListener objects to the Swing components in the GUI via hook methods. These are private nested classes defined in the Controller class that implement ActionListener. The 'ActionPerformed' method in each nested class, in turn, calls a method within the Controller that corresponds to the pertinent action of the Swing component (e.g. button click).
I think I've managed to nail this down. Please comment.
Much appreciated.


Christian
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17257
    
    6

"'JFrame' as it's only parameter is created"
why not just take a FBNGuiCLient instead? Same thing.
Everything else is how I like it.
Mark


Perfect World Programming, LLC - Two Laptop Bag - Tube Organizer
How to Ask Questions the Smart Way FAQ
Christian Garcia
Ranch Hand

Joined: Jan 29, 2002
Posts: 77
My bad. I meant to use an Interface Type for the parameter....caught me sleeping on that one
I'll change it to FBNClientGUI. The only reason I was using going to type it to an interface was to stay true to the 'Interface Programming' methodology.
John Smith
Ranch Hand

Joined: Oct 08, 2001
Posts: 2937

The 'ActionPerformed' method in each nested class, in turn, calls a method within the Controller that corresponds to the pertinent action of the Swing component (e.g. button click).

I have two questions for you:
1) How does your controller retrieve the values from the View (such as origin and destination for search)?
2) Your post title is about GUI decoupling, and you described the Controller-View decoupling, but you didn't mention your Model-View communications.
Eugene.
Juan Katabasis
Ranch Hand

Joined: Jun 20, 2001
Posts: 46
same questions as Eugene here, model part of the mvc shema can not be identified from your notes.
also: is your controller class offering all system functionalities? if so, you have
the controller class with more responsabilities than just being a ' controller ', low cohesion to me.


Regards<br />J.
Christian Garcia
Ranch Hand

Joined: Jan 29, 2002
Posts: 77
Hey guys,
Here's the scoop:

How does your controller retrieve the values from the View (such as origin and destination for search)?

I'm not exactly sure yet. Establishing a means of communication with the GUI via the Controller was my first step and the impetus for this post. My inclination is to use the internal reference to the GUI in the Controller to access the data I need. If you have some suggestions for doing this I'm more than willing to listen/read

Your post title is about GUI decoupling, and you described the Controller-View decoupling, but you didn't mention your Model-View communications.

I've begun coding for each method in the controller to return an instance of my FBNTableModel where applicable. From what I've read here at the Ranch, that was a common design decision. It works for me, so I took the same route. If I misinterpreted it please explain how.

...is your controller class offering all system functionalities? if so, you have
the controller class with more responsabilities than just being a ' controller ', low cohesion to me.

As of now, my Controller is the conduit thru which all data flows via instances of my TableModel. So, I guess the answer to your question is yes.
My understanding is that the Controller's purpose is to service the GUI with a Model. To do so requires the Controller to have access to a remote or local implementation of the Data class. Given that, how would this approach lend itself to "low cohesion"?
Thanks for the help.
Juan Katabasis
Ranch Hand

Joined: Jun 20, 2001
Posts: 46
the controller (or the model or the view) can be a subsystem, you are not enforced to implement it in a lonely class
John Smith
Ranch Hand

Joined: Oct 08, 2001
Posts: 2937

My inclination is to use the internal reference to the GUI in the Controller to access the data I need. If you have some suggestions for doing this I'm more than willing to listen/read.

I actually don't have a good solution. But I sure don't like to see something like this in the controller:
String destination = view.destinationCombo.getSelectedItem().toString();
One alternative approach is to call the controller with parameters from the View. The drawback is, of course, that View now has to know about the controller and its methods. The other solution, perhaps more compromising, is to use public accessor methods in the View, so that the controller can look like this:
String destination = view.getDestinationAirport();
Yet another approach is to make this info a part of the event object.

I've begun coding for each method in the controller to return an instance of my FBNTableModel where applicable.

I am not very excited about this. Firstly, returning a table model firmly plants the idea that the view must be a table, which may not be the case at all. Secondly, the controller should not return anything, according to how MVC works. The controller job is to map user actions into business logic. The controller calls the appropriate method of the model, and it's done. From here, either the model "pushes" the updates to the Views, or the Views "pull" the results from the Model. Or something in between: the model "pings" the views, and the views pull the updates they need.
Eugene.
[ January 24, 2003: Message edited by: Eugene Kononov ]
Christian Garcia
Ranch Hand

Joined: Jan 29, 2002
Posts: 77
Eugene,
You gave me a lot to ponder over. I think that I'm missing something in my understanding of MVC.
What I have is as follows:
Model: My FBNTableModel
Controller: Intermediary b/w model and View
View: The GUI itself.
I'll assume (hope) that the above is at least somewhat correct. Now, I like this approach very much:
...The other solution, perhaps more compromising, is to use public accessor methods in the View, so that the controller can look like this:
String destination = view.getDestinationAirport();

That falls in line with my understanding of using a reference to the View that is held within the Controller to access the data.
I am not very excited about this. Firstly, returning a table model firmly plants the idea that the view must be a table, which may not be the case at all. Secondly, the controller should not return anything, according to how MVC works. The controller job is to map user actions into business logic.

The posts that I've come across show instances of a TableModel being returned from a Controller. Is it then true that this is an incorrect design choice for the FBN project? If so, do you have a recommendation for getting around the Controller & Model knowing of one another?
The controller calls the appropriate method of the model, and it's done. From here, either the model "pushes" the updates to the Views, or the Views "pull" the results from the Model.

Does this mean that data processing should be present within my FBNTableModel?

Thanks for bearing with my on this.
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17257
    
    6

My opinion is that it is fine to have the following in the controller.


here I set a textbox to become empty and a button to disabled. All the objects on the GUI are public in my design. I don't see an issue with that.

I don't see any problem with that and it is only three lines of code, can anyone tell me what the code does? Easy to read huh? This code will self destruct in one hour.
Mark
[ January 24, 2003: Message edited by: Mark Spritzler ]
Christian Garcia
Ranch Hand

Joined: Jan 29, 2002
Posts: 77
Mark,
I've gone about it using something akin to your code. The only difference is that in my tinkering this morning I decided to create a method in my FBNClientGUI that provides access to the JTable.setModel() method. I did this b/c (at the moment) my JTable in the GUI is Private.
In retrospect, using an accessor for a component-specific method couples the Model with the View and I'm sure that's not gonna' fly (no pun intended) with my fellow Ranchers :roll: . Hmmm....well, I'm grasping at straws now b/c I want to keep the components in the GUI private, but still need to access the Table data. If anyone has managed to accomplish this and will share their design decision(s) I will be most grateful.
John Smith
Ranch Hand

Joined: Oct 08, 2001
Posts: 2937

My opinion is that it is fine to have the following in the controller.
...
frontEnd.numberOfPassengersTextBox.setText("");
frontEnd.bookButton.setEnabled(false);
enablePassengerTextField();

I strongly disagree for the following reasons:
1. I think it is a fundamental flaw in the MVC (and the system that uses it), if the controller is given an authority to modify the elements of the view(s). Actually, since your GUI elements are public, it's not just the controller, but any other yahoo object that can modify the GUI at its leisure, if it has a reference to GUI. This violates the very first principle of OO, -- encapsulation. GUI should not expose the mechanics (its element types) of how it displays the data to the outside world. Beyond the side effects of some external object modifying the GUI, you have a larger issue: if you decide to replace your GUI element with some other element (such as replacing a combobox with a textbox), or even change the name of that element, your controller(s) now also need to change.
2. The responsibility of a controller in MVC is to select views, not to monkey with them. For example, if the user clicks a "login" button, it's perfectly acceptable for the controller to intercept the mouse event and to invoke something like loginView.show(). If your GUI elements need to change, the rendering code should be in the GUI itself. After all, it is the GUI that knows best how to render data. You need to keep that rendering code in one logical place -- the GUI.

Model: My FBNTableModel

While I am on the offensive note, I'd like to disagree with that as well . The rational is similar to what I already said, -- low reusability. Just look at the class name itself: it ties the (presumably) pluggable/interchangable application model (which is supposed to be completely separated from the view(s)) with a very specific view component (Jtable). This is very misleading, to say the least, from an MVC point of view. Beyond that, there are very serious consequences if you build your application level MVC around a table model. Under a very reasonable assumption that your business users don't like a table as a rendering component, it will create a huge ripple effect on all three parts of your MVC:
-- the GUI (obviously)
-- the controller (if the methods of controller assume that the model is a JTable model)
-- the model itself (it's not going to be rows and columns anymore, but something completely different).
It's perfectly fine (and very encouraged) to define a FlightsTable (JTable) and FlightsTableModel (its table model). This pair forms its own MVC (or MV). However, it's just a pluggable component that is likely to change, so don't make your application level MVC dependent on it.

If so, do you have a recommendation for getting around the Controller & Model knowing of one another?

Controller does know about the model, since controller invokes the business methods of the model. Perhaps you meant to ask about decoupling the views from the model. If so, my recommendation is something known as a model "push". That is, after a business method of the model modifies the model state, the model "pushes" the updates down to the view(s). Normally, this is accomplished by registering the views as model listeners and requiring the views to implement a "model listening contract". For example:

Notice, among other thing, how independent the view is from the model in the code above. If you decide to replace JTable with something else to show the flights, only one line of code needs to change in the view. The model doesn't need to change at all. Also, if you need to add a view (such as a chart of the flights), you simply add another class that conforms to the contract between the model and the views. Again, nothing else needs to change.
Eugene.
[ January 24, 2003: Message edited by: Eugene Kononov ]
HS Thomas
Ranch Hand

Joined: May 15, 2002
Posts: 3404
Eugene,
Following your prescription above for re-usability (not sticking to Tablemodel as Model)
, can this be achieved with just one View and Controller , because that's all that the assignment needs ?
Scrap this, I've just taken a closer look at your code. It just takes a little longer for all the bits to fall into place.
regards
[ January 25, 2003: Message edited by: HS Thomas ]
fadi mujahid
Greenhorn

Joined: Oct 14, 2002
Posts: 28
I totally agree with you
Christian Garcia
Ranch Hand

Joined: Jan 29, 2002
Posts: 77
Eugene,
I've come to realize the deficiencies in my design . Sometimes in order to learn a 'better' way to do something you need to see the 'wrong' way, so I'll take this as a positive thing.
Foremost, I didn't understand that the Model 'serves' the view(s). Knowing that resolves some confusion I had in regards to its' [the model] role in the MVC architecture. The 'push' technique you described is absolutely the way to go and makes total sense it respect to separating the View(s) from the Model. I also like using the FlightsModel and FlightsTableModel concept as that [I believe] lends itself to the Swing Component architecture.
Okay, having said all that I have 2 questions:
The responsibility of a controller in MVC is to select views, not to monkey with them. For example, if the user clicks a "login" button, it's perfectly acceptable for the controller to intercept the mouse event and to invoke something like loginView.show().

Given this, the Controller then knows all of the views explicitly? Doesn't that create a (potentially) undesirable dependency?


...If you decide to replace JTable with something else to show the flights, only one line of code needs to change in the view

In addition to adding the new component to the View. Correct?
[ January 26, 2003: Message edited by: Christian Garcia ]
Richard Yip
Greenhorn

Joined: Nov 24, 2002
Posts: 11
Hello Guys,
I agree with most of the statements above. However I like to point out what FlightTableModel extends AbstractTableModel actually does.
The FlightTableModel provides information(data) for the views; i.e. JTable for display. The data is this case is dataInfo like this.
public FlightTableModel(ArrayList newDataInfoList) {
This works fine if you don't need any edits/updates.
You can provide ways to edit/update by adding/removing listeners directly the model itself or through some actionListener on the JTable.
I use the second approach. I have created popupmenu that shows a list of task that I can do when clicking on the JTable.
I have a method for any client program to update flight data within the model. It is something like this.
protected void flightChanged(DataInfo dataInfo, int numberOfSeats)
By using this approach, I was able to reuse the model.
Richard
John Smith
Ranch Hand

Joined: Oct 08, 2001
Posts: 2937

Given this, the Controller then knows all of the views explicitly? Doesn't that create a (potentially) undesirable dependency?

Yes, but this dependency is very limited. If you keep the GUI controls private, the controller can only call the view public interface, such as show/hide and (possibly) some accessor methods, such as view.getDestinationAirport(). Notice that if I change or rename the GUI control that holds the destination airport, the controller doesn't need to change, and neither does the model.

flightsTableModel.setData(data)
...If you decide to replace JTable with something else to show the flights, only one line of code needs to change in the view
.. In addition to adding the new component to the View. Correct?

Right, you just throw a new component in your view and replace flightsTableModel.setData(data) with something like flightsListBoxModel.setData(data) or flightsHTMLModel.setData(data). Well, actually, you need to define a setData() method for your new model, but this is completely insulated from everything else.
Just so that it is clear, I want to stress again that to achieve some degree of reusability, you must have a top level (application level) MVC, where the View itself may contain several other mini-MVC, such as JTable/JTableModel. If this embedded mini-MVC changes in a given view, only that specific view needs to be adjusted.
Eugene.
[ January 26, 2003: Message edited by: Eugene Kononov ]
aadhi agathi
Ranch Hand

Joined: Apr 29, 2002
Posts: 263

Eugene.
[ January 24, 2003: Message edited by: Eugene Kononov ][/qb]<hr></blockquote>
Hi Eugene,
i am just curious to know why you havent used the "default Model extends Observable and View implements Observer "

please help me to understand where the default Observer pattern is lagging though the default Observer will require the model to extend Observable and if the Model inturn is extending
something else the default Observer can't be used.
please help me to understand this.
[ January 27, 2003: Message edited by: aadhi agathi ]
[ January 27, 2003: Message edited by: aadhi agathi ]

Aadhi
Christian Garcia
Ranch Hand

Joined: Jan 29, 2002
Posts: 77
Eugene,
From the posts we've traded, I've extrapolated the following:
(1a) A Driver class loads the application
(1b) Create instance of the view
(1c) View registers itself as a ModelListener
(2a) An ActionEvent is triggered in the View via user interaction
(2b) Controller intercepts the ActionEvent via hook methods in the View
(2c) Controller retrieves pertinent data (via the reference to the View) to pass to method calls in the Model.
(2d) Controller calls Model methods that wrap an instance of the Data class.
(3) Processing returns & each ModelListener's 'modelChanged()' method is executed to deliver the updated TableModel (since that's the component being used in this example).
This appears to be the flow from what I've gathered in our discussion. Please let me know if there is something I didn't take into consideration or misinterpreted.
Thanks.
[ January 27, 2003: Message edited by: Christian Garcia ]
John Smith
Ranch Hand

Joined: Oct 08, 2001
Posts: 2937

(1a) A Driver class loads the application
(1b) Create instance of the view
(1c) View registers itself as a ModelListener
(2a) An ActionEvent is triggered in the View via user interaction
(2b) Controller intercepts the ActionEvent via hook methods in the View
(2c) Controller retrieves pertinent data (via the reference to the View) to pass to method calls in the Model.
(2d) Controller calls Model methods that wrap an instance of the Data class.
(3) Processing returns & each ModelListener's 'modelChanged()' method is executed to deliver the updated TableModel (since that's the component being used in this example).

Everything is good except for your last item: when the model state changes, it should push the updated data in a generic form, such as Object. Do not return this data as a reference of some concrete type, such as table model. Instead, let the view decide how to treat it. For example, your code in the View may look like this:

Again, this is for reusability, -- your model should not be required to change if your view changes.
Eugene.
[ January 27, 2003: Message edited by: Eugene Kononov ]
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Decoupling the GUI