Original: http://today.java.net/pub/a/today/2003/10/24/swing.html? Page = 2
Solution: Event Drive Programming
All of these solutions have a common fatal defect - attempt to represent a task's function set while continuously changing threads. However, changing the thread requires an asynchronous model, while the thread processes Runnable asynchronously. Part of the problem is that we have a synchronized model at an attempt to achieve a synchronous model above an asynchronous thread model. This is the root of all runnails and dependence, execution order and internal SCoPing issues. If we can build a real asynchronous, we can solve our problems and greatly simplify the Swing thread. Before this, let's list the questions we have to solve: 1. Execute code in the appropriate thread 2. Use swingutilities.invokelater () asynchronously. Asynchronous execution results in the following problem: 1. Mutual coupling Component 2. Difficulties in variables 3. The order of execution allows us to consider messaging-based systems like Java Message Services (JMS) because they provide loose coupling between functional components in asynchronous environments. The message system triggers an asynchronous event, as described in Enterprise Integration Patterns. Interested participants listen to the event and respond to the event - usually by executing some of their own code. The result is a group of modular, loosely coupled components, and components can be added or removed from the system without affecting other components. More importantly, the dependence between components is minimized, and each component is a good definition and package - each of them is only responsible for their work. They simply trigger the message, and other components will respond to this message and respond to messages triggered by other components. Now, we first ignore the thread problem and decouple the components and transplanted into the asynchronous environment. After we solve the asynchronous problem, we will go back to see thread problems. As we have to see, it will be very easy to solve this problem. Let us take the examples introduced in front and transplant it to an event-based model. First, we call the lookup to a class called lookupManager. This will allow us to remove database logic in all UI classes and ultimately allow us to completely disengage the two. Below is the code of the LookupManager class: class lookupmanager {private string [] lookup (string text) {string [] results = ... // database lookup code return results} Now we start to convert to the asynchronous model. In order to call this call asynchronously, we need to return to the abstract call. In other words, the method cannot return any value. We will have something you want to know in terms of distinctive action. The most obvious event in our example is the search end event. So let's create a listener interface to respond to these events. This interface contains a single method lookupCompleted (). Below is the definition of the interface: Interface looklistener {public void lookCompleted (Iterator Results); This will allow us to pass other information without changing the LookUplistener interface. For example, we can include the strings and results that are found in the lookup of the LOOKUPEVENT.
The following is LookupEvent class: public class LookupEvent {String searchText; String [] results; public LookupEvent (String searchText) {this.searchText = searchText;} public LookupEvent (String searchText, String [] results) {this.searchText = searchText; this .results = results;} public string getsearchText () {return searchtext;} public string [] getResults () {return results;}} Note that the Lookupevent class is not variable. This is very important because we don't know who will deal with these events during the pass. Unless we created an event's protection copy to each listener, we need to make events unstrenomed. If not, a listener may unintentionally or maliciously revisit the event object and destroy the system. Now we need to call the LookUpComplete () event on lookupManager. We start by adding a set of LookupListener on LookupManager: List listeners = new ArrayList (); and a method LookupListener added and removed on the LookupManager: public void addLookupListener (LookupListener listener) {listeners.add (listener);} public void RemoveluggUplistener (LookUplistener Listener) {listener.Remove (Listener);} We need to call the listener's code when the action occurs. In our example, we will trigger a lookupCompleted () event while looking back. This means it iterations on the listener collection and uses a lookupCompleted () method using a lookupevent event object. I like to extract these code to a separate method Fire [event-method-name], which constructs an event object, iterations on the listener collection, and calls the appropriate method on each listener. This helps isolating the code of the primary logic code and calling the listener. Here is our fireLookupCompleted method: private void fireLookupCompleted (String searchText, String [] results) {LookupEvent event = new LookupEvent (searchText, results); Iterator iter = new ArrayList (listeners) .iterator (); while (iter.hasNext ( )) {Lookuplistener Listener = (lookuplistener) iter.next (); listener.lookUpCompleted (Event);}} The second line of code creates a new collection that is incorporated into the original audio collection. This decides on the LookupManager after the listener response event.
If we are not a secure copy collection, a bored error occurred while some listeners should be called without being called. Below, we will call the FirelookUpCompleted auxiliary method when the action is completed. This is the end of the LOOKUP method's return query results. So we can change the Lookup method to trigger an event rather than return a string array itself. Below is a new lookup method: public void lookup (string text) {// mimic the server call delay ... try {thread.sleep (5000);} catch (exception e) {E.PrintStackTrace ();} // Imagine We got this from a server string [] results = new string [] {"book one", "book two", "book three"}; firelookupCompleted (text, result);} Now let us add a listener to lookupManager . We want to update the text area when you look back. In the past, we just call the setText () method directly. Because the text area is executed in the UI together with the database call. Since we have abstracted from the UI, we will use the UI class as a listener to the LookusManager, listen to the Lookup event and update yourself accordingly. First, we will achieve the listener interface class definition: public class FixedFrame implements LookupListener Then we implement interface methods: public void lookupCompleted (final LookupEvent e) {outputTA.setText ( ""); String [] results = e.getResults () ; For (int i = 0; i In order to demonstrate how to add new events, let's add an event starting with a lookup. We can add an event called lookupstarted () to lookuplistener, and we will trigger it before the lookup starts execution. We also create a FirelookupStarted () event call all lookuplistener's lookstarted (). The LOOKUP method is now as follows: Public void lookup (String text) {FirelookupStarted (text); // mimic the server call delay ... try {thread.sleep (5000);} catch (exception e) {E.PrintStackTrace (); } // imagine we got this from a server string [] results = new string [] {"book one", "book two", "book three"}; firelookupCompleted (text, result);} We also add new triggers Method FirelookupStarted (). This method is equivalent to the FirelookUpCompleted () method, except that we call the lookupstarted () method on the listener, and the event does not contain result sets. Here is the code: private void fireLookupStarted (String searchText) {LookupEvent event = new LookupEvent (searchText); Iterator iter = new ArrayList (listeners) .iterator (); while (iter.hasNext ()) {LookupListener listener = (LookupListener) iter .next (); listener.lookupstarted (Event);}} Last, we implements a lookstarted () method on the UI class, setting the text area prompt the current search. Public Void LookupStarted (Final Lookupevent E) {Outputta.Settext ("Searching for:" E.GetSearchText ());} This example shows how easy it is to add new events. Now let's take a look at the flexibility of displaying event-driven decoupling. We will demonstrate by creating a log class while outputting information in the command line when a search start and end. We call this class as Logger. The following is its code: public class Logger implements LookupListener {public void lookupStarted (LookupEvent e) {System.out.println ( "Lookup started:" e.getSearchText ());} public void lookupCompleted (LookupEvent e) {System. Out.println ("Lookup Completed:" " " E.GETRESULTS ());}} Now we add Logger as a listener for the LookusManager in the FixedFrame constructor. public FixedFrame () {lookupManager = new LookupManager (); lookupManager.addListener (this); lookupManager.addListener (new Logger ()); initComponents (); layoutComponents ();} Now that you've seen to add new events, create new Listener - show you the flexibility and scalability of the event driver. You will find that as you have more developed a program, you will be more skilled in your application to create a universal action. Like all other things, this requires only time and experience. It seems that many studies have been made on the event model, but you still need to compare it with other alternatives. Consider development time costs; the most important, this is a one-time cost. Once you have created a listener model and their actions, add a listener to your app in your app. Thread By now, we have solved the above asynchronous problems; through the listener to deliver the components, pass the variables through the event object, the order of execution through the event generation and the synonym registration. Let us return to thread problems, because it is brought here. It is actually very easy: Because we already have an asynchronous listener, we can simply let the listener decide which thread should be executed in. Consider the separation of the UI class and the LookUpManager. The UI class is based on an event and decides what processing needs. And, this class is also swing, and the log class is not. So let the UI class be responsible for determining what it should perform in what thread will make more meaningful. So let's take a look at the UI class again. Below is a LOOKUPCOMPLETED () method without a thread: public lookuote e) {outputta.setText (""); string [] results = E.GETRESULTS (); for (int i = 0; i Swingutilities.Invokelater (new runnable () {outputta.setText (""); string [] results = E.GETRESULTS (); for (int i = 0; i