Abstract
This paper addresses the problem of merging object-oriented and automaton-based
programming technologies. There are two major questions: "how to integrate an
automaton into an object-oriented system" and "how to implement an automaton
in the object-oriented fashion".
The problem of merging object-oriented and automaton-based programming technologies
is often analyzed in the literature [1-4]. This article gives only short description of
of the problem.
Introduction
In a traditional object-oriented system objects are communicating
with each other via their public interfaces. Each interface is a contract
between object and its clients. An interface exposes one or more methods.
A method can be treated as an event (message) with parameters and a return value.
It often happens, that a system contains one or more state-based
objects. The behaviour of these objects depends on their state,
where state can be represented as some scalar value. One of the ways to
represent this behaviour is a finite state machine.
A state-based object can be implemented with a following three-ply structure:
- traditional object-oriented interface;
- intermediate layer that converts methods to events;
- underlying automaton-based logic.
The proposed approach corresponds to the main object-oriented paradigm
principles.
- Encapsulation. The fact of state-based behaviour is hidden from the environment.
- Polymorphism. If there is a number of state-based objects with a
different behaviour but with a common interface, then a user can interact
with them in a uniform manner.
- Inheritance. The behaviour of a state-based object can be extended
using ordinary inheritance mechanism.
Object-oriented interface
There are no any awkward restrictions to state-based objects interfaces.
These interfaces can contain a number of methods. A method can accept a number
of parameters and can have a return value. Some methods can be marked as constant.
For example, the interface intended to control file access rights is shown below.
struct IFileAccess {
virtual void Open( string const& _mode ) = 0;
virtual void Close() = 0;
virtual bool CanRead() const = 0;
virtual bool CanWrite() const = 0;
};
Intermediate layer
This layer is a routine. It is just an interface implementation where each
method call is converted to an event object, the latter is propagated to an
underlying logic layer and then, after processing, the return value and output
parameters are returned to the caller.
This class should be derived from an interface and from the common
implementation facility as shown on the picture below.

Figure 1. Intermediate layer inheritance model
Firstly, a particular event type should be defined for each method. Each
of these types should be derived from StateBased::Event type.
There are no more limitations to the structure of these types. These types
can be declared inside of FileAccessBase class.
class FileAccessBase : public virtual IFileAccess,
protected virtual StateBased {
protected:
struct EOpen : public Event {
EOpen( string const& _mode ) : mode( _mode ) {}
string const& GetMode() const {
return mode;
}
private:
EOpen& operator=( EOpen const& );
string const mode;
};
struct EClose : public Event {};
struct BoolEvent : public Event {
BoolEvent() : result( false ) {}
bool GetResult() {
return result;
}
bool SetResult( bool _result ) {
result = _result;
return true;
}
private:
bool result;
};
struct ECanRead : public BoolEvent {};
struct ECanWrite : public BoolEvent {};
};
Secondly, all methods from the interface class should be implemented.
These implementations have similar structure: the corresponding event
object is created on the stack and passed to
StateBased::Execute() method.
class FileAccessBase : public virtual IFileAccess,
protected virtual StateBased {
public:
virtual void Open( string const& _mode ) {
EOpen event( _mode );
Execute( event );
}
virtual void Close() {
EClose event;
Execute( event );
}
virtual bool CanRead() const {
ECanRead event;
Execute( event );
return event.GetResult();
}
virtual bool CanWrite() const {
ECanWrite event;
Execute( event );
return event.GetResult();
}
};
Automaton-based logic
There is a human readable way to represent automaton's logic -
transition graph. This method is used in Statecharts [5], SyncCharts [6],
SWITCH-technology [7] etc. Each automaton's state in a transition graph is
drawn as rounded rectangle. Each transition between states is drawn as
arrow. Arrows are labeled in <condition>/[<action>] format.
Brackets mean that action part is unnecessary.
One of possible transition graphs for mentioned example is shown below.
This logic allows us to have two file opening modes - reading
and writing.

Figure 2. Transition graph for FileAccess class
There are two major ways to implement automaton:
- using dynamic function table.
- using operator
switch with nested if
conditions (this method is used in SWITCH-technology).
This paper provides a new one. The approach is based on a built-in
virtual methods mechanism. The space of automaton's states is mapped on the
virtual methods set. These methods are called state-methods. All
state-methods have the same signature. Each automaton's state corresponds
to the only one state-method and vice versa. The instance of state-based
object holds current state as a reference to the current state-method.
There is a special state-method called main, which is treated
as an initial state.
With these presumptions the automaton from Fig. 2 can be easily
implemented as following.
class FileAccess : public FileAccessBase {
protected:
virtual bool main( Event& _event ) {
if ( EOpen* e = EventCast< EOpen >( _event ) ) {
if ( e->GetMode() == "r" )
return SetState( *this, &FileAccess::reading );
else if ( e->GetMode() == "w" )
return SetState( *this, &FileAccess::writing );
}
return false;
}
virtual bool reading( Event& _event ) {
if ( ECanRead* e = EventCast< ECanRead >( _event ) )
return e->SetResult( true );
else if ( ECanWrite* e = EventCast< ECanWrite >( _event ) )
return e->SetResult( false );
else if ( EClose* e = EventCast< EClose >( _event ) )
return SetState( *this, &FileAccess::main );
return false;
}
virtual bool writing( Event& _event ) {
if ( ECanRead* e = EventCast< ECanRead >( _event ) )
return e->SetResult( false );
else if ( ECanWrite* e = EventCast< ECanWrite >( _event ) )
return e->SetResult( true );
else if ( EClose* e = EventCast< EClose >( _event ) )
return SetState( *this, &FileAccess::main );
return false;
}
};
Each state-method should return true if transition is done
and false otherwise. It is accepted that if there is no any
transition done for a method call, then
StateBase::UnexpectedOperation exception is thrown.
Logic extension via inheritance
The main advantage of the proposed approach is the possibility of
using inheritance. The behaviour of an object in a particular state can be changed or
extended. New states can be added to the automaton.
For example, we can extend our FileAccess object by adding
a mixed read/write mode. The corresponding automaton is shown below.

Figure 3. Transition graph for RWFileAccess class
The asterisk over state main means that this state is
extended, i.e. all the transitions from main to other states
are saved in the new automaton. The code for extended version of file
access control looks like following.
class RWFileAccess : public FileAccess {
protected:
virtual bool main( Event& _event ) {
if ( FileAccess::main( _event ) )
return true;
if ( EOpen* e = EventCast< EOpen >( _event ) )
if ( e->GetMode() == "rw" )
return SetState( *this, &RWFileAccess::readwriting );
return false;
}
virtual bool readwriting( Event& _event ) {
if ( ECanRead* e = EventCast< ECanRead >( _event ) )
return e->SetResult( true );
else if ( ECanWrite* e = EventCast< ECanWrite >( _event ) )
return e->SetResult( true );
else if ( EClose* e = EventCast< EClose >( _event ) )
return SetState( *this, &RWFileAccess::main );
return false;
}
};
Moreover, a multiple inheritance is also available. The logic of a
several automatons can be joined. There are two major requirements for this
option:
- the interface and
StateBased class must be inherited virtually;
- the initial state-method
main should be overridden to avoid ambiguity.
For example, suppose we have AppendFileAccess class with
following transition graph.

Figure 4. Transition graph for AppendFileAccess class
The code for this class is shown below.
class AppendFileAccess : public FileAccessBase {
protected:
virtual bool main( Event& _event ) {
if ( EOpen* e = EventCast< EOpen >( _event ) )
if ( e->GetMode() == "a" )
return SetState( *this, &AppendFileAccess::appending );
return false;
}
virtual bool appending( Event& _event ) {
if ( ECanRead* e = EventCast< ECanRead >( _event ) )
return e->SetResult( false );
else if ( ECanWrite* e = EventCast< ECanWrite >( _event ) )
return e->SetResult( true );
else if ( EClose* e = EventCast< EClose >( _event ) )
return SetState( *this, &AppendFileAccess::main );
return false;
}
};
Further, we can join AppendFileAccess and
RWFileAccess into a single state based object that can work in
four modes - reading, writing,
readwriting and appending. The corresponding
automaton looks as following.

Figure 5. Transition graph for RWAFileAccess class
There is a very laconic code for this class.
class RWAFileAccess : public RWFileAccess, public AppendFileAccess {
protected:
virtual bool main( Event& _event ) {
if ( RWFileAccess::main( _event ) )
return true;
if ( AppendFileAccess::main( _event ) )
return true;
return false;
}
};
Back-end facility
There is a back-end class called StateBased. All the
state-based classes must be derived from it. The StateBased
class provides following:
- base class for all event objects;
- const and non-const versions of
Execute() method which is
used by intermediate layers;
- SetState() method which is used to change the object's state;
EventCast() template method which is used to determine
particular type of an event object.
Maintaining immutability
Unfortunately, the automatic control of object immutability is broken.
During the construction of a state-based object instance the non-const
reference to it is remembered. And even if you call a const interface
method - the non-const reference is used to call state-method.
However, StateBased class automatically controls the
immutability of object's state in runtime. If const interface method is
called and current state-method tries to call SetState()
method, then StateBased::ReadOnlyViolation exception will be
thrown.
But if there are some user data in the class, then immutability should
be maintained manually. If non-const access to the data is requested and
StateBased::IsImmutable() is true, then
ReadOnlyViolation should be thrown.
Conclusion
The proposed approach is well suitable under following conditions:
- there are many objects with the same interface but with different behaviour;
- the hierarchy of logic can be introduced for this objects;
- there is a little of data in objects beside their states (no any data is an ideal case).
References
- Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides Design Patterns - Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.
- Kevlin Henney State Government, C/C++ Users Journal C++ Experts Forum, June 2002
- Ted Faison Object-Oriented State Machines, Software Development Magazine, Sept, 1993.
- Paul Adamczyk The Anthology of the Finite State Machine Design Patterns, The 10th Conference on Pattern Languages of Programs 2003
- Harel D. Statecharts: A visual formalism for complex systems. Sci. Comput. Program., vol. 8, June 1987. pp. 231-274.
- Andre C. Representation and Analysis of Reactive Behaviors: A Synchronous Approach. CESA'96, Lille, France, IEEE-SMC, July 1996.
- Shalyto A.A. Switch-technology. Algorithmization and programming of tasks of logical control. SPb.: Nauka (Science), 1998.