Communicating Precise Object Interface Behavior in Complex Domains *

David Fleming

Department of Statistics and Computer Science
West Virginia University
Morgantown, WV 26506-6330
Email: fleming@cs.wvu.edu
http://www.cs.wvu.edu/~fleming

Jim Wagner

Department of Computer and Information Science
The Ohio State University
Columbus, OH 43210
Email: wagner@cis.ohio-state.edu
http://www.cis.ohio-state.edu/~wagner

Abstract:

Typical separation of component interfaces from their implementations plays at least one important role in software development. The separation makes it possible to develop multiple concrete components that are syntactically compatible with the same interface, and the compatibility is compiler-checkable. The ``plug-compatibility'' of multiple implementations for a single interface greatly augments the reusability of the interface and its (many) concrete implementations.

Unfortunately, syntactic descriptions of interfaces are inadequate for communicating to programmers what the components do. This inadequacy often goes unnoticed essentially because the typical example components such as stacks, lists, and queues are ``well understood'' and are familiar to most audiences. Of course, previous familiarity or common understanding do not characterize components in complex domains where it becomes essential for object interfaces to communicate with their users [4].

Keywords: Communication, complex domains, formal specification, object interfaces

Workshop Goals: To arrive in Columbus without having car trouble, and see what happens from there.

Working Groups: Rigorous Specification, Reuse and Product Lines, Component Certification Tools.

* This research at West Virginia University is funded in part by grants DAAH04-94-G-0002 and DAAH04-96-1-0419, monitored by the U.S. Army Research Office.

Background

Ask any two software engineers what operations will be included in an interface specification for a generic stack, and they will at least reply with ``push'' and ``pop''. Better yet, give them a generic stack interface declaration in C++, such as:

   template <class Item> class Stack_Template {
      public:
         virtual void Push (Item& x);
         virtual void Pop (Item& x);
         virtual void Clear ();
         virtual int  Depth_Of ();
      private:
         ...
   }

Next, ask them to tell you what this component does. They will probably provide a mutually agreeable description regarding the behavior of this stack component. This is because, well, everybody knows what a stack does.

Now, give them a C++ interface for an ``engine management'' component, such as:

   template <
      class Engine_Type,
      class Vehicle_Params,
      class Target_Performance_Params
   >
   class Engine_Manager {
      public:
         virtual void RPM_Range_Selector (
               Engine_Type& et,
               Target_Performance_Params& tpp
            );

         virtual int Loaded_Output (
               Engine_Type& et,
               Vehicle_Params& vp
            );

         virtual bool Is_Optimal ();
            ...

      private:
         ...
   }

Next, ask them what this component does. Then, stand back as the answers fly! Determining what this component does from this interface description is hopeless. What is the meaning of RPM_Range_Selector? What Is_Optimal? Optimal gas mileage? Optimal horsepower? What is meant by ``Loaded''? In fact, probably the only agreement you'll get from the two software engineers is that your question is loaded!

Position

The two example component interfaces above illustrate an important point. That is, even though ``standard'' components exist and operation names can be very suggestive in their own right, there can be no implicit contract as to the behavior of software components in general. Even for the ``well understood'' stack component above, it is not obvious, for example, whether Push preserves the value of its argument.

We therefore take the position that, to clearly and precisely communicate what a software component does, an explicit description of its behavior is needed [5]. This description should augment ``common knowledge'' by providing precise definitions of terms which are taken for granted in understanding typical data structure components. Natural language descriptions, in addition to being inherently verbose, are not suitable for precise communication. A solution to this difficult problem is the use of common definitions, terms, and theories from mathematics such as made possible by notations like RESOLVE [3].

RESOLVE as a Component Description Language

The idea of having explicit and precise descriptions for software components is hardly new. Booch, for example, states in his discussion on a ``stack abstract data type'', that ``we would like to express these (behavioral) characteristics more formally'' [1]. He goes on to say, ``current abstract data type techniques are not sufficiently powerful (for this)''.

A central goal of RESOLVE is to address this deficiency by providing means for formally and easily describing the behavior of reusable software components, among others. Consider the example component interface specification below, given in the formal specification language of RESOLVE:

   concept  Mystery_Template
      context
         global context
            facility  Standard_Integer_Facility

         parametric context
            type  Entry

      interface
         type  Mystery_Type is modeled by string of math [Entry]
            exemplar  m
            initialization
               ensures  m = empty_string

         operation  Some_Op_1 (
                  alters   m : Mystery_Type
                  consumes   x : Entry
               )
            ensures   m = <#x> * #m

         operation  Some_Op_2 (
                  alters   m : Mystery_Type
                  produces   x : Entry
               )
            requires   m /= empty_string
            ensures   #m = <x> * m

         operation  Some_Op_3 (
                  alters   m : Mystery_Type
               )
            ensures   m = empty_string

         operation  Some_Op_4 (
                  preserves   m : Mystery_Type
               ) : Integer
            ensures   Some_Op_4  =  |m|

   end  Mystery_Template

This is a model-based specification, in which the behavior is modeled using a mathematical type. For Mystery_Template, this model is a mathematical string. An implementation of this component will provide a ``correspondence'' that captures the relationship between concrete values of Mystery_Type and its abstract (string) values.

The context section describes the context in which the component will operate. Mystery_Template says that it will require the use of the type ``Integer'' (Standard_Integer_Facility). Mystery_Template also requires an ``Entry'' parameter for instantiation. Hence, Mystery_Template is a generic ``something''.

The interface section of Mystery_Template exports a type ``Mystery_Type'' and four operations that act upon values of this type. The type is modeled by a mathematical string whose elements are the mathematical model of the Entry (math [Entry]). The initialization ensures clause states that variables of this type are initially empty.

The four operations have requires and/or ensures clauses that state the preconditions that must be met and the postconditions that will be fulfilled, respectively, in terms of the mathematical (string) model. The ensures clause is only guaranteed when the requires clause is satisfied. Additionally, there are parameter ``modes'': alters, preserves, consumes, and produces.

What behavior does Mystery_Template describe?

The specification of Some_Op_1 ensures that the new string value (m) will be the mathematical concatenation of the incoming value of the Entry (#x) and the incoming value of the string (#m) (concatenation is denoted by an asterisk, the incoming value by a #, and a string of a single entry is enclosed in ``less than-greater than'' signs). That is, the string is augmented by the new Entry on the left end.

The specification of Some_Op_2 requires that the string value of m being passed in is not the empty string. It ensures that the new string (m) will be the old string (#m) with the left-most (and produced) Entry (x) removed. That is, the string is shortened by one Entry on the left end, and this Entry is returned as the value of x.

The specification of Some_Op_3 ensures that the value returned for Mystery_Type variable m will be the empty string. That is, it will be cleared.

The specification of Some_Op_4 ensures that an Integer value representing the number of elements in the string m will be returned. That is, the length of the string is returned.

Inspection of the behavior of Mystery_Template reveals that it behaves abstractly like a generic stack component. Even though it mentions nothing of the word ``stack'', its specification is superior to the ``Stack_Template'' described in the first section. For example, it is known precisely that ``Push'' (Some_Op_1) consumes its parameter, rather than preserves it.

How the Stack component might be specified in RESOLVE/C++

Much work has been done to blend the conceptual framework, discipline, and language of RESOLVE with widely-used programming languages. In 1992 a RESOLVE/Ada blend was the subject of a dissertation by Joseph Hollingsworth [2]. Currently the Reusable Software Research Group at The Ohio State University is working on a RESOLVE/C++ blend. In RESOLVE/C++ the interface specification of Stack would look something like:

template <
   class Entry
>
class  Stack_Template
    /*!
        is modeled by string of math[Item]
        exemplar self
        initialization
            ensures self = empty_string
    !*/             
{
public:
  Stack_Template () {};
  virtual ~Stack_Template () {};

  virtual void operator &= (/*alters*/ Stack_Template& rhs) = 0;
        /*!
            ensures self = #rhs  and  rhs = #self
        !*/

  virtual void Push (/*consumes*/ Entry& x) = 0;
        /*!
            ensures self = <#x> * #self
        !*/

  virtual void Pop (/*produces*/ Entry& x) = 0;
        /*!
            requires |self| > 0
            ensures  #self = <x> * self
        !*/

  virtual void Clear () = 0;
        /*!
            ensures  self = empty_string
        !*/

  virtual int Depth_Of () = 0;
        /*!
            ensures Depth_Of = |self|
        !*/

private:
  /* Implicit assignment and copy constructor are prohibited */
  Stack_Template& operator = (const Stack_Template& rhs);
  Stack_Template (const Stack_Template& m);
};

Summary

The purpose of the Mystery_Template, of course, is not to emphasize the use of meaningful (or meaningless) names for objects and operations. Instead, it is to make it clear that when objects in complex and less understood domains are described syntactically and informally, they are likely to be mysterious no matter how good the choices of names. Suitable names are valuable, but for such domains, descriptions based on well-understood mathematical notations are fundamentally necessary.

A programming component specification is most easily and precisely understood when it is given in a language expressly designed for making specifications of programming components. This we see with the Mystery component specified in RESOLVE.

On the other hand, a concrete specification in a programming language, such as the C++ specification for the Stack template, is terse and incomplete. Even if one examines the body of the specification, where the details for each of the operations are given, one cannot really be sure of all intentions the designer had in mind.

When you use both a specification language and a programming language to design and implement components, you are faced with the task of keeping both in synchronization.

When we blend the ideas of a specification language in with a programming language, we obtain a hybrid which is more complex than either the specification language or the programming language. We can see this with the RESOLVE/C++ abstract template specification for Stack. But such a hybrid holds out the promise of clear specifications as an integral part of the programming environment.

References

[1] G. Booch, Software Components with Ada, Benjamin/Cummings, 1987.

[2] J.E. Hollingsworth, Software Component Design-for-Reuse: A Language-Independent Discipline Applied to Ada, Ph.D. diss., OSU-CISRC-1/93-TR01, Dept. of Comp. and Inf. Sci., Ohio State Univ., Columbus, Aug. 1992.

[3] M. Sitaraman and B.W. Weide, eds., ``Special Feature: Component-Based Software Using RESOLVE'', ACM SIGSOFT Software Eng. Notes 19, 4 (October 1994), pp. 21-67.

[4] M. Sitaraman, ``Role of Conceptualizations and Generalizations in Domain Modeling'', NASA Focus on Software Reuse Workshop, September, 1996.

[5] B.W. Weide, Component-Based Software Engineering, draft manuscript.

Biography

David Fleming is a doctoral candidate studying Computer Science at West Virginia University. His interests are primarily in software engineering and software reusability. He has authored and co-authored papers on these topics. He is a member of the ACM.

Jim Wagner is a graduate student in Computer and Information Science at The Ohio State University. His interests are in programming languages, algorithms, component reusability, software engineering, and the theoretical foundations of computer science.