Part 1 of this three-part article reviews the basics of object-oriented programming and summarizes the challenges it presents for high-integrity programming. Part 2 will provide a primer on the Ada programming language, and Part 3 will detail the tools Ada offers to help developers meet the OOP challenges.
The software development methodology known as object-oriented programming (OOP) has been available in programming languages for more than forty years. Although heavily used in many kinds of systems, OOP has not been widely adopted by developers of high-integrity software — i.e., where the highest levels of safety and/or security are required. This community has traditionally judged OOP’s benefits, such as component reuse and ease of maintenance, to be outweighed by its costs, which include personnel training, risks in moving to a new technology, and uncertainty on how to comply with safety or security certification. This situation is changing, however. With high-integrity systems growing in size and scope, OOP’s advantages in the design of robust and extensible software architectures are looking increasingly attractive. As a result, stakeholders such as regulatory authorities, software developers, and system integrators are focusing on how to use OOP safely and securely.
A specific and significant example is the avionics industry. The DO-178B safety certification standard1 is being adapted to address OOP issues, and the upcoming DO-178C standard2 contains a supplement3 with guidance on how to use object-oriented and related technologies while avoiding their vulnerabilities. Let's take a closer look at the basics of object orientation and the challenges OOP presents for high-integrity programming.
Concepts of Object Orientation
Object orientation is a software design and implementation approach that organizes a system around the kinds of entities (classes) that it deals with, and their interrelationships. The functions that the system performs are taken into account when designing the classes with which the functions are associated.
The essence of object orientation is embodied in three principal concepts: class, object, and encapsulation. A class is a language feature or combination of features that integrate a module, or unit of program composition, with a template for data instances. A class defines a set of members, which comprise the state (data fields) and behavior (invokable operations) of each instance. An operation has a signature (operation name and parameter specifications) and an implementation (code body). An object is an instance of a class. Encapsulation is the ability to restrict access to a class’s members, especially the state data, so that they are only available to those parts of the program with a need to know.
Systems can be designed with an object-oriented architecture if the programming language simply supports the concepts of class, object, and encapsulation, but the real power of object-oriented programming comes from several additional features: inheritance, polymorphism, and dynamic dispatching.
Inheritance is the ability to define a new class (the subclass) as an extension of an existing class (the superclass). The subclass implicitly includes the superclass’s fields, can add new operations and/or fields, and either implicitly inherits or explicitly overrides existing operations from the superclass. An inheritance hierarchy for a class consists of that class and all its direct or indirect subclasses.
Abstract classes are often useful as roots of inheritance hierarchies. An abstract class does not permit object creation. An abstract class can define abstract operations; i.e., operations with signatures but no implementation.
Inheritance in general is also called implementation inheritance, since the subclass implicitly inherits the state data of the superclass as well as the implementation of the superclass’s operations (unless overridden). If the superclass is abstract and sufficiently simple — no state data, and only abstract operations — then inheritance is known as interface inheritance (see figure 1). Such a class is commonly known as an interface. An interface may be regarded as a contract that needs to be fulfilled, or a set of operations that need to be implemented, by each non-abstract subclass.
Figure 1: This class diagram shows a class Node, an interface Encryptable, and a class DataNode that inherits from Node and implements Encryptable. DataNode implicitly inherits the Id field and the Set_Id and Id operations from Node, overrides Put and Get from Node, and implements Encrypt and Decrypt from Encryptable.
A subclass may exhibit single inheritance (only one superclass) or multiple inheritance (arbitrary number of superclasses). Languages vary in how these are modeled. For example C++ supports multiple implementation inheritance, while Ada, Java, and C# support multiple interface inheritance but single implementation inheritance.
Polymorphism is the ability of a variable to denote an object from any class in a given inheritance hierarchy. Dynamic dispatch (also known as dynamic binding) means interpreting an operation on a polymorphic variable as the version of the operation defined by the class of the object denoted by the variable at run time. In contrast, static binding means interpreting an operation on a variable as the version of the operation defined by the type (class) of the variable at compile time.
Proper use of OOP can make a system easier to implement and maintain, especially in dealing with new requirements, since adding a class to an inheritance hierarchy does not require changes or even recompilation of existing classes. However, OOP also introduces new challenges for software verification, especially when safety certification is required.