CroftSoft /
Library /
Tutorials
Interface Mail
David Wallace Croft
2008 Jan 27 Sun
Java interface Mail is used as an intermediary to broadcast and retrieve
messages passed between objects. Class FlipMail is an implementation of
interface Mail which flips the outgoing and incoming messages lists when
updated. FlipMail simplifies desktop application programming
by reducing coupling between Model, View, and Controller (MVC) objects and
controlling when state change request and event messages are processed.
This tutorial is a continuation of the message-oriented programming (MoP)
tutorials
Interface Slot and Interface Seq. Interface Mail
is simply an extension Slot and Seq.
package com.croftsoft.core.util.mail;
import com.croftsoft.core.role.Slot;
import com.croftsoft.core.util.seq.Seq;
/**********************************************************************
* An interface for sending and receiving messages.
*
* @since
* 2008-01-27
* @author
* <a href="https://www.croftsoft.com/">David Wallace Croft</a>
**********************************************************************/
public interface Mail<Message>
extends Seq<Message>, Slot<Message>
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
{
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
}
Interface Seq provides read-only access to a sequence of messages via
its get() method. Interface Slot provides write-only
access to send a message via its offer() method.
With a reference to a Mail instance, an object can both broadcast and
retrieve messages.
public ExemplarModelImp ( final Mail<ExemplarMessage> mail )
///////////////////////////////////////////////////////////////////////
{
NullException.check ( this.mail = mail );
}
Since this communication is indirect, an object can communicate with other
objects without requiring references to those other objects. Instead of
passing references to recipient objects as constructor method arguments or
attaching them as listeners, simply provide each object with a reference
to a Mail instance as a constructor argument. Since objects do not have
references to each other, there is also no need to nullify references and
remove listeners when an object is deleted.
public void update ( )
///////////////////////////////////////////////////////////////////////
{
final int size = mail.size ( );
for ( int i = 0; i < size; i++ )
{
final ExemplarMessage exemplarMessage = mail.get ( i );
final ExemplarMessage.Type type = exemplarMessage.getType ( );
switch ( type )
{
case INCREMENT_CLICK_COUNT:
clickCount++;
mail.offer ( ExemplarMessage.CLICK_COUNT_CHANGED_INSTANCE );
break;
default:
// ignore
}
}
}
In the preceding code listing, an object both retrieves and broadcasts a
message during its update phase. In this case the object retrieves a
request to change its internal state and then broadcasts an event message
upon doing so. Note that there is no setter method to call; change
requests must be relayed via Mail. Note also that
there are no methods to add and remove listeners; all events are posted to
Mail. This zero coupling between communicating objects is a key benefit of
message-oriented programming (MoP).
package com.croftsoft.core.util.mail;
import java.util.*;
import com.croftsoft.core.lang.lifecycle.Updatable;
/**********************************************************************
* Replaces incoming messages with outgoing messages when updated.
*
* @since
* 2008-01-27
* @author
* <a href="https://www.croftsoft.com/">David Wallace Croft</a>
**********************************************************************/
public final class FlipMail<Message>
implements Mail<Message>, Updatable
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
{
private List<Message>
incomingList,
outgoingList,
swappingList;
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
public FlipMail ( )
///////////////////////////////////////////////////////////////////////
{
incomingList = new LinkedList<Message> ( );
outgoingList = new LinkedList<Message> ( );
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
public Message get ( final int index )
///////////////////////////////////////////////////////////////////////
{
return incomingList.get ( index );
}
public int size ( )
///////////////////////////////////////////////////////////////////////
{
return incomingList.size ( );
}
public boolean offer ( Message message )
///////////////////////////////////////////////////////////////////////
{
return outgoingList.add ( message );
}
public void update ( )
///////////////////////////////////////////////////////////////////////
{
swappingList = incomingList;
incomingList = outgoingList;
outgoingList = swappingList;
outgoingList.clear ( );
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
}
Class FlipMail is an implementation of interface Mail which is backed by
two LinkedList objects, incomingList and outgoingList. Between updates,
objects read messages from the incomingList and post messages to the
outgoingList. Note that the incomingList is strictly read-only and the
outgoingList is write-only.
When the update() method is called, the references to the incomingList and
outgoingList are swapped. The outgoingList is then cleared. The effect is
that the previously read messages in the incomingList are discarded and the
previously inaccessible messages in the outgoingList become available.
final Updatable [ ] updatables = new Updatable [ ] {
flipMail,
exemplarModel,
exemplarView,
exemplarController };
Once per loop, the update() method of each of your communicating objects
are called so that they can retrieve and broadcast messages. The update()
method of your FlipMail object is also called to replace
the old messages with the new messages. I update FlipMail before the
Model-View-Controller (MVC) objects so that any pending requests to
initialize state become available during the first loop.
Because state change request messages and event messages posted to the
outgoingList during the current loop are not processed until the next loop,
it is much less likely that your program will have the problem of model
state being changed while it is being read. Another problem that is
avoided is having your program lock up in a runaway loop in which an event
triggers a state change request which then triggers the same event again.
It also makes it easier to debug your program by examining the messages
stored in FlipMail at each loop transition.
If you find yourself passing references between MVC classes excessively or
you find it difficult to debug a sequence of method calls
between tightly coupled classes, I recommend that you consider moving to
a message-oriented programming (MoP) style instead. If you can imagine
objects without setter methods, you are on your way to the simplicity of
zero coupling.
|