Abstract event systems

Instructor's Guide


intro polymorphism idioms patterns events summary, Q/A, literature
To conclude this chapter about idioms and patterns, we will look at a somewhat more detailed example employing (user-defined) events to characterize and control the interaction between the objects. The example is taken from  [Henderson93]. The abstract system, or repertoire of statements indicating the functionality of our application is depicted in slide as-system.
  th = new centigrade();
  th = new fahrenheit();
  th.set(f);
  f = th.get();
  
For thermometer th, th1; float f;
slide: Abstract system -- thermometers

First, we will define the functional behavior of the system (in this case a collection of thermometers that record and display temperature values, as characterized above). Then we will introduce the user interface classes, respectively to update the temperature value of a thermometer and to display its value. After that we define a concrete event class (derived from an abstract event class) for each of the possible kinds of interactions that may occur. Then, after installing the actual objects comprising the system, we will define the dependencies between (actual) events, so that we can guarantee that interactions with the user will not result in an inconsistent state.

Functional behavior

A thermometer must provide the means to store a temperature value and allow for the changing and retrieving of this value. The temperature values are assumed to be stored in degrees Kelvin.
  class thermometer { 
thermometer
protected thermometer( float v ) { temp = v; } public void set(float v) { temp = v; } public float get() { return temp; } protected float temp; };

slide: The thermometer class

Since only derived classes can use the protected constructor, no direct instances of thermometer exist, so the class is abstract. We will distinguish between two kinds of thermometers, measuring temperatures respectively in centigrade and fahrenheit.
  class centigrade extends thermometer { 
centigrade
public centigrade() { super(0); } public void set(float v) { temp = v + 273; } public float get() { return temp - 273; } };

slide: The centigrade class

The class centigrade redefines the methods get and set according to the measurement in centigrade, and in a similar way we may define the class fahrenheit.
  class fahrenheit extends thermometer { 
fahrenheit
public fahrenheit() { super(0); } public void set(float v) { temp = (v - 32) * 5/9 + 273; } public float get() { return temp * 9/5 + 32 - 273; } };

slide: The fahrenheit class

Both the thermometer realization classes take care of performing the conversions necessary to store and retrieve the absolute temperature value.

User interface

We will define two simple interface classes, of which we omit the implementation details. First, we define the interface of the displayer class, needed to put values to the screen.
  class displayer extends window { 
displayer
public displayer() { ... } public void put(String s) { ... } public void put(float f) { ... } };

slide: The displayer class

And secondly, we define a prompter class, which defines (in an abstract way) how we may get a value from the user (or some other component of the system).
  class prompter extends window { 
prompter
public prompter(String text) { ... } public float get() { ... } public String gets() { ... } };

slide: The prompter class

Together, the classes displayer and prompter define a rudimentary interface which is sufficient to take care of many of the interactions between the user and the system.

Events

To define the interactions with the user (and their possible consequences) we will employ events, that is instances of realizations of the abstract event class, defined below.
  abstract class event { 
event
pubic void dependent(event e) { ... } pubic void process() { ... } public void operator(); // abstract method private event[] dep; };

slide: The event class

Since a simple event (for example, the modification of a value) may result in a series of events (needed to keep the system in a consistent state), an event object maintains a set of dependent events, which may be activated using the process method. Further, each class derived from event is assumed to define the application operator, that is the actual actions resulting from activating the event. The first realization of the abstract event class is the update event class, which corresponds to retrieving a new temperature value from the user.
  class update extends event { 
update
public update(thermometer th, prompter p) { _th =th; _p = p; } void operator()() { _th.set( _p.get() ); process(); } thermometer _th; prompter _p; };

slide: The update class

An update involves a thermometer and a prompter, which are stored when creating the update event object. Activating an update event instance results in retrieving a value from the prompter, setting the thermometer to this value and activating the dependent events. In a similar way, we define the second realization of the abstract event class, the show event class, which corresponds to displaying the value of a thermometer.
  class show extends event { 
show
public show(thermometer th, displayer d) { _th = th; _d = d; } public void operator() { _d.put( _th.get() ); process(); } thermometer _th; displayer _d; };

slide: The show class

Activating a show event instance results in retrieving a value from the thermometer, putting that value on display and activating the events associated with this event.

The installation

The next step we must take is to install the application, that is to create the objects comprising the functional behavior of the system, the user interface objects and (finally) the various event objects.
  thermometer c = new centigrade();
  thermometer f = new fahrenheit();
  
  displayer cd = new displayer("centigrade");
  displayer fd = new displayer("fahrenheit");
  
  prompter cp = new prompter("enter centigrade value");
  prompter fp = new prompter("enter fahrenheit value");
  
  show sc = new show(c,cd);
  show sf = new show(f,fd);
  
  update uc = new update(c,cp);
  update uf = new update(f,fp);
  

slide: Installing the objects

Having created the objects, we are almost done. The most important and perhaps difficult part is to define the appropriate dependencies between the respective event objects.
  uc.dependent(sc);
  uc.dependent(sf);
  uf.dependent(sc);
  uf.dependent(sf);
  

slide: Assigning dependencies

As shown above, we declare the event of showing the value of the centigrade thermometer (and also of the fahrenheit thermometer) to be dependent upon the event of updating the value of the centigrade thermometer. And we repeat this declaration for the event of updating the value of the fahrenheit thermometer. We may now allow the user the choice between updating the centigrade or fahrenheit thermometer temperature value, for example by inserting these events in a menu, as indicated below
  menu.insert(uc);
  menu.insert(uf);
  
The reader is urged to do some mental processing to check that updating the value of one thermometer actually results in changing the value displayed for the other thermometer as well.

Discussion

Organizing interactions with the user (and the other components of the system as well) by means of events provides a powerful way in which to control the consequences of one particular (kind of) interaction. The advantage of such an approach is that the repertoire of possible interactions can easily be extended or modified without affecting the other parts of the system (the parts realizing the functional behavior of the system and the particularities of the user interface). From the perspective of design, it is a good alternative for defining {\em behavioral compositions} (and its corresponding protocol of interaction) in a more or less formal way. See also section Cooperation.