The design of modular agents that work well within a complex environment requires observing a number of issues that are
specific to multi-agent designs. This document serves as a guideline to deal with them and provides suggestions for
their solution.
Dependency Breaking
In object-oriented design, it is common to denote information flows between two classes structurally by connecting the
two classes by an association and adding a public getter-method. In the example below, a class Robot can access
information about new work units from a class Storage by simply calling the method storage.getNewWorkUnit().
Such a dependency between classes can not be readily used in multi-agent system design. This is due to the fact that
agents run in different execution environments and interaction is message-based. Internally, a statement such as storage.getNewWorkUnit() dereferences the association to storage and calls the getNewWorkUnit(), returning a reference to an object of type WorkUnit. In a message-based system
with agents running in different exection environments, this inferred semantics is not valid. Instead, a message
would have to be sent to the storage agent, requesting the information. The storage would have to serialise this
information and return it to the requesting agent. The requesting agent could then deserialise the information and work
with it. Any changes to the deserialised object would, however, not affect the original object. Thus, instead of a
call-by-reference (or call-by-sharing) evaluation, a true call-by-value is performed. A corresponding code snippet
looks like this:
Message m = sendMessage(storage.agentIdentifier, "getNewWorkUnit");
WorkUnitModel wum = m.waitForReply();
Note the use of WorkUnitModel instead of WorkUnit. This is due to the fact that in many cases, not the actual object is
serialised but rather a stripped down version that only contains some of the attributes of the original class to limit
the required communication bandwidth. This is especially useful if the class contains many internal state variables
that do not necessarily have to be restored after the transfer. The class diagram corresponding to the design
just described looks like this:
Note that the robot does no longer have a direct association with the storage, but has access to its agent identifier
through a model. The getNewWorkUnit() method is marked as external, indicating that the method is part of the external
interface of the storage. The stereotypes used here are part of the PosoMAS Auxiliary UML Profile.
The designer has to make a conscious decision whether this level of detail is required in the design models. If a
common understanding of the problem and of the solution approach exists within the development team, it might be easier
to design a regular class diagram that does not take these peculiarities into account. Instead, they could be dealt
with by a model-to-model transformation or similar means. However, if the dependencies can not always be broken by the
same mechanism, it might be worthwhile to have a more detailed design model in which these problems have already been
resolved.
|