HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

It's true that you don't need a computer degree to know how to program. However, to do it with the kind of quality that allows for easy maintenance and change is another matter. As we all know, based on Software Engineering (SE) principles, a software product's life cycle consists of analysis, design, implementation and testing. Most people seem to spend the majority of their time on the latter two phases while forgetting about or putting less emphasis on the former two. This is wrong, especially in the implementation phase. Implementation should constitute the smallest part of the whole development time. Part of the reason why programmers have to spend so much time and effort on the implementation - and then later on the testing - is from not having a good design in the first place.

In this article I'm assuming that the requirement analysis phase of software development is fully understood so I can focus this article on the design of software products.

According to object-oriented analysis and design (OOAD) in SE principles, objects in a system should exhibit the two most important properties: being highly cohesive and lowly coupled. There are various ways to design a highly cohesive and lowly coupled object in Java. The way that I'm sharing with you in this article is based on the Java 1.1 Delegation Event Model.

I'm naming the objects radio broadcaster and radio listener, based on their roles in the design. The reason I'm calling them radio broadcaster and radio listener is because of the similiarity between how these objects work and how radio broadcasting stations play a role in our daily lives.

For example, in this country (or, within a software product) anybody (or, an object) can set up a radio station and become a radio broadcaster. In this way, the radio broadcaster (or, object) becomes a source of information (or, becomes the provider of new service for the system) for the listeners (or, objects) in need of that information.

Since in real life radio programs change every day, even though the interests of radio listeners tend to be the same (or at least, they don't change as often as radio programs do), the radio broadcaster's role is reserved for those objects that constitute the dynamic part of the software components.

Therefore, a new employee who has just joined a company or who hasn't had enough experience with an existing software product can be suitable for developing radio broadcaster objects. It can be a child or a subclass that provides new services for the system.

By now you must have realized that the radio listener's role is assigned to the software components, and that radio listeners are akin to those objects or classes that construct the core components or base framework of the whole software system. These radio listeners are the objects that are most likely to be stable and that require the least number of changes (if any) to the software system. A parent (superclass) or a container object that needs to be extended to provide new functionality is one of those objects.

This analogy, however, is not rigid, because in this design model any object can be a radio listener or a radio broadcaster. Remember that it's always good to plan early and to choose wisely so as not to affect the total development efforts in the later phases.

Keeping this analogy in mind, let's see how we're going to implement this design. In order for radio broadcasters to "transmit" events to radio listeners, they have to implement a custom interface called a RadioBroadcaster interface. Just like any radio listener who wants to "tune in" to the information sent by a radio broadcaster, the listener has to implement a Radio-BroadcastListener interface (see Listing 1).

So, for example, if a class A needs to become a radio broadcaster, class A would implement RadioBroadcaster interface and define its corresponding interface methods (see Listing 2).

The benefit of having a RadioBroadcast object inside RadioBroadcaster is obvious - one doesn't have to redefine all the methods of RadioBroadcast class in all the objects that choose to become radio broadcasters if there are any changes! RadioBroadcast class can be modified (if there's a need to do so) without affecting the radio broadcaster objects as long as the signatures remain the same.

To make life simple, let's assume that class B would like to become a radio listener class and is interested in listening to anything broadcast by class A. So class B implements RadioBroadcastEventListener, as seen in Listing 3.

The two interfaces make use of the following broadcasting channel classes in the process of event communications, i.e., RadioBroadcast and RadioBroadcast-Event - which are mainly extended from java.util.EventObject (see Listing 4).

You may wonder why a class called ServiceBroker suddenly cropped up in the example of class B, acting as a radio listener. For the moment, let's just assume that the class will return an object reference to class A. (The real justifications will become clear later on.)

Sounds simple, right? Yup, but a problem arises. One may ask, What happens if an object needs service or data immediately and without delay? That's a question that specifically involves a term called nonblocking operation. In a nonblocking operation (method in this case), data and service have to become available before a method returns or continues processing. As is well known, event propagation takes time. So, the time when service and data will become available (based on the radio broadcast events) is unpredictable.

In solving this problem, I've come up with a way to always make service and data available to a requesting object. An object that provides a service need not be a radio broadcaster or a radio listener, but it's a duty for this object to keep on updating (as neccessary) the type of data required for any interested objects. This data is handled and managed by another intermediate object called a ServiceBroker.

Once data is made available and is managed by a ServiceBroker, any requesting objects will then retrieve the data directly from the service broker without going through a radio broadcasting channel. This eliminates the process of a source (or, radio broadcaster) having to send an event to a listener, and also eliminates the overhead of having to wait or listen for the answering data (or, feedback) from the listener.

Do we deviate from our original radio broadcaster/listener analogy here? Not at all. In real life, whenever a radio station wants to get feedback on the popularity of a certain program that's been broadcast to the public, the station will hire an agent or agency to get this information (or, feedback) and report on it. This feedback agency is akin to the ServiceBroker object. Radio stations don't visit your house to get information, but they do hire agencies to get the feedback they need. Thus, based on this analogy, the role of a service broker can't be ruled out. Listing 5 shows an example of ServiceBroker codes.

I suppose at this point you can understand why we used ServiceBroker.getClass-A().addRadioBroadcastListeners(this). And there's another benefit to using ServiceBroker. If you take a look at the class B source code that we've previously discussed, you'll find that before listening to an event generated by a radio broadcaster object, there's no need to know what type of object it belongs to! So once again: if in the future the role played by class A, as a radio broadcaster for class B, is replaced by class C, then you won't have to change the codes in class B. This is because when class B calls ServiceBroker.get-ClassA(), it won't know that a ServiceBroker has returned class C instead of class A - as long as the method signature is the same.

The other issue that should be taken into consideration before using this design model is performance. It's indeed slower, due to event passings relatively compared with direct method invocations. However, I think this design model is much better and worth the effort since it saves a lot of problems with maintaining the codes in later development stages.

One may need to consider which part of the software will use this design model and which part won't, as it can impact the performance if used incautiously. Programmers should realize that with this design model, the messages sent by a radio broadcaster object will have to be unique, otherwise they'll be interpreted by the wrong radio listener! The way I've suggested is to use a naming convention such as packagename.classname.eventname. Not having different sets of event channels for different groups of objects will save programmers a lot of effort.

One last rule of thumb (from my experience) is that you should make the radio listener class an inner class of class A if it's to process the radio broadcasting events generated from within class A (called intra-events), and put it outside of class A if it's to handle radio broadcasting events received from outside of class A (called external events).

This MVC-style design not only allows programmers to have highly cohesive and lowly coupled objects, but it also allows programmers to decouple the GUI of an application from its data model. Thus the user interface of an application can be thoroughly revamped without the need for any changes to the underlying data model of the application. All swing components are implemented with this design pattern in mind.

In short, you add tremendous value to your company or client by knowing how to design an application, not just build it. Without a methodology covering the details of how you'll partition your application, you're bound to make costly mistakes. Therefore, you should design your system out of small, independent objects. Also, since smaller modules are less complex, new employees can better learn and understand their tasks by being able to focus on the relevant object. And, as they work on more objects over time, they'll be able to develop a broader understanding of the system.

Resource:
Using the JDK 1.1 Delegation Event Model: sunsite.compapp.dcu.ie/IJUG/javaone/sessions/slides/TT21/events-title.html

About the Author
E Ming Tan was the chief architect of paging and intranet applications at Singapore Telecom. He has worked with Renaissance Software on a JDBC-based Java application, and has worked with LearningByte International - a company that provides customized JFC-based training programs.

	

Listing 1:

public interface RadioBroadcaster {Radio-Broadcast broadcast = new RadioBroad-cast(); 

public voidaddRadioBroadcastListeners-(RadioBroadcastEventListener bel);

public boolean removeRadioBroadcast-Listeners(RadioBroadcastEventListener bel);

public void notifyRadioBroadcastListeners-(RadioBroadcastEvent be);}

public interface RadioBroadcastEvent-Listener extends java.util.EventListener

{public void broadcastPerformed(Radio-BroadcastEvent event);}


Listing 2:

class A implements RadioBroadcaster {.... [any other codes here] ...

//=== defines the method to broadcast events public void
 notifyRadioBroadcast-Listeners(RadioBroadcastEvent be)

{broadcast.notifyRadioBroadcastListeners-(be);}

public void addRadioBroadcastListeners-(RadioBroadcastEventListener bl)

{broadcast.addRadioBroadcastListeners-
(bl); }

public boolean removeRadioBroadcast-Listeners(RadioBroadcastEventListener bl)

{return broadcast.removeRadioBroadcast-Listeners(bl); }

//=== ‘radio broadcast’ an event Radio-BroadcastEvent evt = new
 RadioBroadcast-Event("packagename.A.EventName");

notifyRadioBroadcastListeners(evt);

.... [any other codes here] ... }


Listing 3:

class B implements RadioBroadcast-EventListener { ...[any other codes]...

//=== ‘tune in’ to a certain channel (events transmitted) 
public void broadcast-Performed(RadioBroadcastEvent be) 
{ if (be.getEventID().equals("packagename.A.EventName")) 
{ ...[do something here as required by application logic]... } 
else if (be.getEventID().equals("anyPackageName.anyClassName.anyEventName")) 
{ ...[do something here as required by application logic]... } : 
: ...[any other if else statements]... } 
public void addRadioBroadcastListeners-(RadioBroadcastEventListener bl) 
{ broadcast.addRadioBroadcastListeners(bl); }

public boolean removeRadioBroadcast-Listeners(RadioBroadcastEventListener bl)
 { return broadcast.removeRadioBroadcast-Listeners(bl); }

//=== register itself (class B) as one of the radio listeners

ServiceBroker.getClassA().addRadioBroadcastListeners(this);

...[any other codes]...

//=== remove itself (class B) from listening if there is a need to do so
 ServiceBroker.getClassA().removeRadioBroadcastListeners(this);

...[any other codes]... }


Listing 4:

class RadioBroadcast { Vector listeners = new Vector();

public void notifyRadioBroadcastListeners-(RadioBroadcastEvent be) 
{ Enumeration notifyList = listeners.elements();

while (notifyList.hasMoreElements()) {
 ((RadioBroadcastEventListener)notifyList.nextElement()).broadcastPerformed(be); } }

public void addRadioBroadcastListeners-(RadioBroadcastEventListener bl) { listeners.addElement(bl); }

public boolean removeRadioBroadcast-Listeners(RadioBroadcastEventListener bl) { return
 (listeners.removeElement(bl)); } }

public class RadioBroadcastEvent extends java.util.EventObject implements
 java.io.Serializable { private String eventID;

public RadioBroadcastEvent(String eventID) {super(eventID); this.eventID = eventID;}

public String getEventID() { return eventID;} }


Listing 5:

public class ServiceBroker { private static JLayeredPane layeredPane = null;
private static SMediaPlayer mediaPlayer = null;

private static UserProfile uProfile = null;

private static A classA = null;

public final static int AUDIO_MANUAL = 0;

public final static int AUDIO_AUTO = 1;

public final static int VIDEO_MANUAL = 0;

public final static int VIDEO_AUTO = 1;

...[any other declaration codes]...

/** Returns the user profile of the user who has successfully logged in */ public static
 UserProfile getUserProfile() { return uProfile; }

/** Sets the current user profile to the user profile of the user id who has successfully logged in, 
@param CurrentUserId: 
the User id whose user profile is to be used to set the property 
*/ public static void setUser-Profile(String CurrentUserId)
{ user_list = (UserList)Broker.loadObject(sUtil.getUserDir()+SignOn.USERLIST_PATH);

uProfile = user_list.getUser(CurrentUserId);

currentUser = uProfile.getUserID();

uProfile.setUserSessionFileName(currentUser+"001"); }

public static JLayeredPane getLayered-Pane() { return layeredPane; }

/** Returns object of class A */ public static A getClassA(){ if(classA == null){ classA = new A();

classA.init(); }

return classA; }

...[any other codes]... }


      
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.