Difference between revisions of "EventThread Pattern"

From Developer Documents
Jump to navigation Jump to search
m
m
Line 2: Line 2:
 
When designing an event-listener model, the developer has to make three decisions. First, should notifications use listeners or events. Second, should notifications be processed immediately or queued for later handling. Third, should notifications be handled in the very current thread or some other. ''EventThread'' coding pattern addresses the second and the third question. Not only are both cases solved with the same simple solution, but also - instead of implementation, the ''caller'' of the object can decide which model to use. Here goes.
 
When designing an event-listener model, the developer has to make three decisions. First, should notifications use listeners or events. Second, should notifications be processed immediately or queued for later handling. Third, should notifications be handled in the very current thread or some other. ''EventThread'' coding pattern addresses the second and the third question. Not only are both cases solved with the same simple solution, but also - instead of implementation, the ''caller'' of the object can decide which model to use. Here goes.
  
====Event Thread====
+
====Event Thread Pattern====
In EventThread pattern, the Listener/Observer interface has a function that allows the implementation to decide the executing environment of the event. Java ([http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html Executor]) is an interface that has various default implementations (See [http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html Executors]). Work can be executed in current thread, executed in new thread, or placed in a work queue.  
+
In EventThread pattern, the Listener/Observer interface has a function that allows the implementation to decide the executing environment of the event. Java ([http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html Executor]) is an interface that has various default implementations (See [http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html Executors]). Many models are supported. Work can be executed in current thread, executed in new thread, or placed in a work queue, or directed to an EDT (EventDispatchThread).  
  
 
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
Line 26: Line 26:
 
obj.addListener( new MyListener() { ... } );
 
obj.addListener( new MyListener() { ... } );
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
====Variation====
 +
In an variation, the listener doesn't have ''getExecutor()'', but instead the executor environment is given as argument to observable's ''addListener(Listener, Executor)''.
 +
<syntaxhighlight lang="java">
 +
 +
public class MyObservableObject {
 +
...
 +
void addListener(MyListener listener, Executor executor);
 +
}
 +
public interface MyListener {
 +
void onEvent(Object sender, Object event);
 +
}
 +
 +
obj.addListener( myListener, CURRENT_THREAD );
 +
obj.addListener( myListener, myWorkQueue );
 +
obj.addListener( myListener, AWT_EDT ); // or SWT_EDT
 +
obj.addListener( myListener, Executors.newSingleThreadScheduledExecutor() );
 +
</syntaxhighlight>
 +
  
 
====Null is current thread====
 
====Null is current thread====
Line 47: Line 66:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 
 
  
  
Line 95: Line 112:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
====Variation====
 
In an variation, the listener doesn't have ''getExecutor()'', but instead the executor environment is given as argument to observable's ''addListener(Listener, Executor)''.
 
<syntaxhighlight lang="java">
 
 
public class MyObservableObject {
 
...
 
void addListener(MyListener listener, Executor executor);
 
}
 
public interface MyListener {
 
void onEvent(Object sender, Object event);
 
}
 
 
obj.addListener( myListener, CURRENT_THREAD );
 
obj.addListener( myListener, myWorkQueue );
 
obj.addListener( myListener, Executors.newSingleThreadScheduledExecutor() );
 
</syntaxhighlight>
 
  
 
--
 
--
  
 
[[User:Toni Kalajainen|Toni Kalajainen]]
 
[[User:Toni Kalajainen|Toni Kalajainen]]

Revision as of 12:32, 7 January 2011

When designing an event-listener model, the developer has to make three decisions. First, should notifications use listeners or events. Second, should notifications be processed immediately or queued for later handling. Third, should notifications be handled in the very current thread or some other. EventThread coding pattern addresses the second and the third question. Not only are both cases solved with the same simple solution, but also - instead of implementation, the caller of the object can decide which model to use. Here goes.

Event Thread Pattern

In EventThread pattern, the Listener/Observer interface has a function that allows the implementation to decide the executing environment of the event. Java (Executor) is an interface that has various default implementations (See Executors). Many models are supported. Work can be executed in current thread, executed in new thread, or placed in a work queue, or directed to an EDT (EventDispatchThread).

<syntaxhighlight lang="java"> public class MyObservableObject { ... void addListener(MyListener listener); } public interface MyListener {

void onEvent(Object sender, Object event);

/** * Get the executor environment where the event will be handled. * * @return executor */ Executor getExecutor();

}

MyObservable obj = ... ; obj.addListener( new MyListener() { ... } ); </syntaxhighlight>

Variation

In an variation, the listener doesn't have getExecutor(), but instead the executor environment is given as argument to observable's addListener(Listener, Executor). <syntaxhighlight lang="java">

public class MyObservableObject { ... void addListener(MyListener listener, Executor executor); } public interface MyListener { void onEvent(Object sender, Object event); }

obj.addListener( myListener, CURRENT_THREAD ); obj.addListener( myListener, myWorkQueue ); obj.addListener( myListener, AWT_EDT ); // or SWT_EDT obj.addListener( myListener, Executors.newSingleThreadScheduledExecutor() ); </syntaxhighlight>


Null is current thread

For syncronous object, you can design your interface so that null value denotes current thread. It is more convenient to use and you can optimize the implementation with one less Runnable objects.

<syntaxhighlight lang="java"> public interface MyListener {


void onEvent(Object sender, Object event);

/** * Get the executor environment where the event will be handled. * null value denotes that the events is handled immediately * and in the caller's thread. * * @return executor or null */ Executor getExecutor();

} </syntaxhighlight>


CURRENT_THREAD

There are many implementations supported in the Executors except the one that runs in the current thread. You can have it with a few lines of code.

CURRENT_THREAD handles events right-away in current thread, which is also the most typical case.

<syntaxhighlight lang="java"> public static Executor CURRENT_THREAD = new Executor() { public void execute(Runnable command) { command.run(); } };

public class MyListenerImpl implements MyListener {

public void onEvent(Object sender, Object event) { ... }

public Executor getExecutor() { return CURRENT_THREAD; } }

</syntaxhighlight>


Adapter

Some listeners have default implementation called adapter (For example AWT Listeners & Adapters). With this pattern it is a good idea to have a default executor in the adapter.

<syntaxhighlight lang="java"> public abstract class MyAdapter implements MyListener {

public Executor getExecutor() { return CURRENT_THREAD; } }

MyObservable obj = ... ; obj.addListener( new MyAdapter() { @Override public void onEvent(Object sender, Object event) { ... } } ); </syntaxhighlight>


--

Toni Kalajainen