http://www.developer.com/

Back to article

Creating Dynamic Swing Views with Reflection by Extending MVC


September 29, 2003

Often large GUI projects need to be deployed quickly and adhere to strict guidelines; therefore design and implementation process is assigned to multiple developer groups doing the work simultaneously. With the arrival of Model–View–Controller (MVC) architecture, several developer groups can work on business logic or model layer, data representation or views, and Controller independently; combining the pieces at the end. MVC has been used extensively in GUI and Web–Based applications; in this article I’ll show how to extend MVC architectural pattern to build a Swing GUI program by using Reflection to dynamically generate Views. The Controller will read a property file, generate Views, and corresponding Models, and execute simple business logic. Developers working on Views and following this design will only need to update properties file and their new class instances will automatically be added to the main project, without the need to modify Controller or any other part of the project. Reflection will be used to instantiate new objects and call required methods.

On a historical note, model–view–controller architectural pattern was conceived in the mid–1980's by developers of the Smalltalk-80 GUI library. It has since then been extensively used in most object oriented user interfaces, it has also been extensively covered in numerous books and publications. According to the MVC pattern, the model should be separated from the actual implementation of its view. The reason being, that there could be many representations of the same data and closely coupling views with the model would complicate project’s development, modifications and create maintenance nightmares.

In other words, you do not want to change your business logic every time you change your view.

When working on a large Swing GUI project, one way to represent views is to use Tabs or JTabbedPane as an underling class. Development of different modules should be assigned to different programmer groups and at the end and each Tab (view) has to be added to the main application. And before any coding is done, project architects usually come up with object relationship diagrams, use-case diagrams, object flow diagrams and hand out coding responsibilities. At the same time, GUI programmers or designers create appropriate screen prototypes for all sections.

Sample Project

In order to show how to extent MVC to create and coordinate views, I have created a simple GUI project that shows an application containing several tabs and several buttons with attached actions; this project has all the components of a template for a large–scale projects, and with small modifications can easily be reused in other GUI applications. The project contains – a controller that, as any MVC controller, deals with all data flow and coordinates object relationships, but on top of that it also literally creates all views. A model layer – represented by interface ModelInterface and several concrete classes extending it, and a view layer – represented by abstract class AbstractView (that by itself extends generic JPanel component) and several concrete tab classes extending AbstractView class. The model layer is actually a set of beans that perform some rudimentary business logic operations. The beans are not EJBs, but simply objects that manipulate data. The project also has one main property file.

In an enterprise level application properties are usually read as java.util.Properties and include data-store locations, connection pool information, environment variables, debug levels and other global information. For our purposes only views info in a text file will suffice. Additional services managed by controller like persistence, distribution or security, are also beyond the scope of this article.

Main Controller

The Controller is the brain of the project, its responsibility is to capture user events and to determine which actions each of these events imply, and in my case to instantiate new View objects and corresponding Model objects and coordinate actions between them. I called the controlled class “MainController”, it has three methods: init – where all the initialization and views instantiation takes place, execute – where an action from a View invokes some operation from a corresponding Model, and loadProperties method that reads a text file with names of the concrete Views to add. The text properties file is called smvc.property and has just three lines – the actual names of concrete views or tabs:

ConcreteView1
ConcreteView2
ConcreteView3

MainController also has three fields: a main JTabbedPane to add new Views too, an array of Tabs and a HashMap of to store Model objects.

In the init method I call the loadProperties("smvc.properties") to fill the array with tab names from the property file. As each view’s class name is read from the properties file, I use Java Reflection API to instantiate actual objects and add them to the main JTabbedPane. See listing 1 at the end of this article.

{
Class newClass = Class.forName((String) tabs.get(i));
//try to make an object – extending AbstractView
AbstractView anyTab = (AbstractView) newClass.newInstance();
//add our new view tab here, and set its name
mainPane.addTab(anyTab.getName(), anyTab);

// I also set a reference back to the Controller
// let each tab know who is its controller
anyTab.setParent(this);
}

As soon as I add all dynamic Tabs, we can see them:


Swing Views

As I said, each Tab (view) in the project is a subclass of AbstractView abstract class. The AbstractView itself has methods to set the Tab name, set parent component (reference to the controller object), and init method that automatically uses Class name as Tab name (you can change that of cause); very useful since concrete Views will inherent them all.

(See source listing for complete AbstractView code).

// get the name of the class - for tab name
String name = this.getClass().getName();
// get rig of all package info just leave class name
setViewName(name.substring(name.lastIndexOf('.') + 1));

I created several concrete view classes; each concrete View class calls an init method of its super class to set its name and adds a button to invoke a simple action from a corresponding Model, via MainConntoller object. Usually a view layer implements a rendering of the model, but in our case I will only call an action. The view is responsible for getting and showing information from the corresponding model that is appropriate based the model state. The view also gathers information to pass on to the model via controller. If I wanted too, I could’ve added some fields in the each Model and got their data when a matching action was requested from my Tab.
When the button on concrete View 1 is pressed, it will call MainController execute method (via its reference to parent we set earlier) and pass a String key or Model name to let the Controller know to which model it should delegate the request. Code below demonstrated this.

//inside anonymous class for action listener attached to a button
public void actionPerformed(java.awt.event.ActionEvent e) {
   ((MainController) ConcreteView1.this.parent).execute(ConcreteView1.this,"Model1");
}

I’m passing a reference to the View itself [ConcreteView1.this] as an optional parameter, but if I wanted too I could’ve used it to retrieve user data from the View and done something with it. MainController will instantiate any Model, if it has not done it yet, by using Reflection, add it to the HashMap, and call its action method. (See MainController execute method source.) I add each new Model objects to the HashMap right after instantiation, for the obvious reason not to instantiate a new object every time there is a request from the View. Therefore, if a new request comes, I first check if object already exists and only then complete action. See listing 2.

Model object is created once and is reused afterwards.

Please note that the model does not have any dependence on views or Controller. Generally speaking model layer encapsulates application domain’s concepts, both behavior and state. It responds to requests for information about its state and responds to instructions to change its state. In a well–designed project each concrete Model class will be based on some business logic need and may involve extensive operations on the data or a RDBMS access.

Models should also have clear and simple public interfaces so that if a view is completely changed, for instance from my Swing JTabbPane to a web JSPs, same methods wound be called and requests made.

Model Layer

I created a public ModelInterface interface class, and inherited it by all concrete Model classes. The only public method is doAction and it simply prints out which Model it belongs to. When Controller delegated the request from the View it calls this method.

….
ModelInterface model = (ModelInterface) models.get(model_key);
if (model == null) {
      Class newClass = Class.forName((String) model_key);
      //try to make an object
      ModelInterface anyModel = (ModelInterface)
      newClass.newInstance();
      //try to add our new view tab here.
      models.put(model_key, anyModel);
      model = anyModel;
}
….
model.doAction();
….

(See Controller source for more into)

The model beans don’t really do anything fancy, but you could effortlessly add more methods to the ModelInterface and their implementing in the concrete classes and then call them based in the key passed on through the controller.

As you can see, this project now has all the elements that can be used to expand View layer with more view. If I want to add another View, all I have to do is create a new class that extends AbstractView, add a new line in the property file for instance “NewTab”, call super.init() in View’s constructor and I’m done! The controller will create it, add it to the main application and show it. It’s that easy! See listing 3.


The incorporation of Model–View–Controller design architecture allowed for decoupling of presentation layer and logic it represents, and extension of MVC allowed for dynamic View generation. Naturally, once the controller is done and coding guidelines have been established, such as properties file format and inheritance structure, work on any project following this design can be concentrated on business logic and presentation by multiple developer groups, simultaneously. Reflection played a big part in automatic view generation, but if class encapsulation were preserved, developers working on the Views or Models would not need to know how their classes are instantiated, added to the main components, referenced and displayed.

Listing 1

Listing 2

Listing 3


Download source code jar here

About the Author

Vlad Kofman is a System Architect currently working on projects under government defense contracts. He has also been involved with enterprise level projects for major wall-street firms and US government. His main interests are object oriented programming methodologies and design patterns.

References

Sun Reflection API by Dale Green
http://java.sun.com/docs/books/tutorial/reflect/

WebSphere Studio Application Developer Version 5 Programming Guide
By Ueli Wahli, Ian Brown, Fabio Ferraz, Maik Schumacher, and Henrik Sjostrand
IBM Redbook SG24-6957-00, May 9, 2003
http://www.redbooks.ibm.com/

Core Java 2, Volume II: Advanced Features (5th Edition)
by Cay Horstmann, Gary Cornell
Publisher: Prentice Hall; 5th edition (December 10, 2001)

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date