Architecture & Design Creating Dynamic Swing Views with Reflection by Extending MVC

Creating Dynamic Swing Views with Reflection by Extending MVC

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)

Latest Posts

Related Stories