Chapter 5: Object-Oriented Programming in ADA Programming Language

Object-Oriented Programming (OOP) is a programming paradigm that uses “objects” — data structures consisting of data fields and methods together with their interactions — to design applications and computer programs. ADA programming language, while being known for its strength in systems programming and safety-critical applications, also supports object-oriented programming. In this chapter, we’ll delve into how Ada incorporates OOP concepts, how it handles inheritance and polymorphism, and how it manages exceptions.

Understanding OOP Concepts in ADA Programming Language

Ada supports the three pillars of object-oriented programming: encapsulation, inheritance, and polymorphism.

Encapsulation

Encapsulation is about bundling related data and procedures together into a single unit, typically a package in Ada. The concept of private types in Ada allows us to hide the implementation details of a type, exposing only what’s necessary to the users of the type.

package Altitude_Control is
    type Altitude_System is private;

    procedure Set_Altitude (Control : in out Altitude_System; Altitude : Integer);
    function Get_Altitude (Control : Altitude_System) return Integer;

private
    type Altitude_System is record
        Altitude : Integer := 0;
    end record;
end Altitude_Control;

Here, we define a private Altitude_System type. We also define two procedures: Set_Altitude and Get_Altitude. These procedures are the only ways for external users to modify or access the altitude value within the Altitude_System object, providing encapsulation of the data.

Abstraction

In aerospace software, it’s common to work with various types of aircraft, each with unique characteristics. Let’s consider the concept of an Aircraft, and how we might use Ada’s abstraction capabilities.

type Aircraft is tagged record
    ID : String (1..20);
end record;

procedure Fly (A : Aircraft) is
begin
    Put_Line (A.ID & " is flying.");
end Fly;

In this case, Aircraft is a tagged type or class in the object-oriented sense, with an ID property and a Fly behavior. This abstract representation can be extended for more specific aircraft types.

Polymorphism and Inheritance

In Ada, we can extend the Aircraft type to represent specific types of aircraft, for example, a Fighter_Jet. We can also override the Fly procedure for the Fighter_Jet, showcasing polymorphism.

type Fighter_Jet is new Aircraft with record
    Weapon_System : String (1..20);
end record;

overriding procedure Fly (F : Fighter_Jet) is
begin
    Put_Line (F.ID & " is flying at supersonic speed.");
end Fly;

In this snippet, Fighter_Jet is a new type that inherits from Aircraft and adds an extra Weapon_System component. We also override the Fly procedure for Fighter_Jet, which means when we call Fly on a Fighter_Jet object, Ada will use the version of Fly specifically designed for Fighter_Jet.

This shows how Ada supports inheritance and polymorphism, fundamental features of OOP, with its system of tagged types and type extension.

Inheritance and Polymorphism in ADA: Aerospace Example

The object-oriented nature of Ada makes it suitable for modeling complex systems like those found in aerospace software. We can use inheritance to define a base type and then create extended types to represent different entities. Let’s illustrate this with an example of a Flight Control System (FCS).

First, we define a base Aircraft type. Each aircraft has a Model and Altitude and a behavior, Fly, which increases the Altitude by a certain increment.

with Ada.Text_IO; use Ada.Text_IO;

package Aircraft_Pkg is
    type Aircraft is tagged record
        Model : String(1..20);
        Altitude : Integer := 0;
    end record;

    procedure Fly (A : in out Aircraft; Alt_Increment : Integer);
end Aircraft_Pkg;

package body Aircraft_Pkg is
    procedure Fly (A : in out Aircraft; Alt_Increment : Integer) is
    begin
        A.Altitude := A.Altitude + Alt_Increment;
        Put_Line (A.Model & " is flying, Altitude: " & Integer'Image(A.Altitude) & " feet.");
    end Fly;
end Aircraft_Pkg;

Next, we can create a new Fighter_Aircraft type that extends the Aircraft type, adding a Weapon_System and Speed. We can also override the Fly method to include additional behavior, such as an increase in speed.

with Ada.Text_IO; use Ada.Text_IO;
with Aircraft_Pkg; use Aircraft_Pkg;

package Fighter_Aircraft_Pkg is
    type Fighter_Aircraft is new Aircraft with record
        Weapon_System : String (1..20);
        Speed : Integer := 0;
    end record;

    overriding
    procedure Fly (F : in out Fighter_Aircraft; Alt_Increment : Integer; Speed_Increment : Integer);
end Fighter_Aircraft_Pkg;

package body Fighter_Aircraft_Pkg is
    procedure Fly (F : in out Fighter_Aircraft; Alt_Increment : Integer; Speed_Increment : Integer) is
    begin
        F.Altitude := F.Altitude + Alt_Increment;
        F.Speed := F.Speed + Speed_Increment;
        Put_Line (F.Model & " is flying at " & Integer'Image(F.Speed) & " mph, Altitude: " & Integer'Image(F.Altitude) & " feet.");
    end Fly;
end Fighter_Aircraft_Pkg;

We now have a Fighter_Aircraft type with its own Fly procedure, showcasing polymorphism. Let’s create a fighter aircraft and make it fly:

with Ada.Text_IO; use Ada.Text_IO;
with Aircraft_Pkg; use Aircraft_Pkg;
with Fighter_Aircraft_Pkg; use Fighter_Aircraft_Pkg;

procedure Main is
    F : Fighter_Aircraft := (Model => "F-22", Weapon_System => "AIM-9 Sidewinder", Altitude => 0, Speed => 0);
begin
    F.Fly(10000, 600); -- Fly with 10000 feet altitude increment and 600 mph speed increment
end Main;

In this code, we create a Fighter_Aircraft object F and make it fly using its own Fly procedure. The output would be: “F-22 is flying at 600 mph, Altitude: 10000 feet.”

This detailed example demonstrates how Ada’s system of tagged types and type extension can be used to implement complex object-oriented patterns, such as inheritance and polymorphism, in the context of aerospace software.

Inheritance Example

Let’s construct a detailed example using different types of aircraft with different engine configurations and flying capabilities.

-- File: Aircrafts.ads
package Aircrafts is
    type Aircraft is tagged record
        Model : String (1..20);
        Altitude : Integer := 0;
    end record;

    procedure Fly (A : in out Aircraft; Alt_Increment : Integer);

    type Propeller_Aircraft is new Aircraft with record
        Propeller_Diameter : Float;
    end record;
    
    overriding
    procedure Fly (P : in out Propeller_Aircraft; Alt_Increment : Integer);

    type Jet_Aircraft is new Aircraft with record
        Number_of_Engines : Integer;
    end record;

    overriding
    procedure Fly (J : in out Jet_Aircraft; Alt_Increment : Integer);
end Aircrafts;

-- File: Aircrafts.adb
package body Aircrafts is
    procedure Fly (A : in out Aircraft; Alt_Increment : Integer) is
    begin
        A.Altitude := A.Altitude + Alt_Increment;
        Put_Line ("Generic aircraft " & A.Model & " is flying at an altitude of " & Integer'Image(A.Altitude) & " feet.");
    end Fly;

    procedure Fly (P : in out Propeller_Aircraft; Alt_Increment : Integer) is
    begin
        P.Altitude := P.Altitude + Alt_Increment;
        Put_Line ("Propeller aircraft " & P.Model & " with propeller diameter " & Float'Image(P.Propeller_Diameter) & " inches is flying at an altitude of " & Integer'Image(P.Altitude) & " feet.");
    end Fly;

    procedure Fly (J : in out Jet_Aircraft; Alt_Increment : Integer) is
    begin
        J.Altitude := J.Altitude + Alt_Increment;
        Put_Line ("Jet aircraft " & J.Model & " with " & Integer'Image(J.Number_of_Engines) & " engines is flying at an altitude of " & Integer'Image(J.Altitude) & " feet.");
    end Fly;
end Aircrafts;

-- File: Main.adb
with Ada.Text_IO; use Ada.Text_IO;
with Aircrafts; use Aircrafts;

procedure Main is
    -- create a generic aircraft
    A : Aircraft := (Model => "Boeing", Altitude => 0);
    
    -- create a propeller aircraft
    P : Propeller_Aircraft := (Model => "Cessna 182", Altitude => 0, Propeller_Diameter => 76.2);

    -- create a jet aircraft
    J : Jet_Aircraft := (Model => "Boeing 747", Altitude => 0, Number_of_Engines => 4);
begin
    A.Fly(5000);
    P.Fly(7000);
    J.Fly(35000);
end Main;

In this example, we have a base type Aircraft with a Fly method. Two derived types, Propeller_Aircraft and Jet_Aircraft, extend the Aircraft type and override the Fly method. The Main procedure creates objects of each type and calls their Fly method.

Each Fly method prints a message that includes specific details of the aircraft, showing polymorphic behavior. For instance, the Fly method for Propeller_Aircraft includes the Propeller_Diameter in its message, while the Fly method for Jet_Aircraft includes the Number_of_Engines.

This example illustrates how Ada supports inheritance, where derived types can inherit the properties and behavior of a base type, and polymorphism, where methods of the same name in different types can behave differently based on the specific type of the object.

Let’s continue the aerospace software example we used earlier with the Aircraft and Fighter_Aircraft types. For this demonstration, we’ll add another level of inheritance with a Stealth_Fighter aircraft. The Stealth_Fighter aircraft is a more specialized type of Fighter_Aircraft with added stealth capability.

with Ada.Text_IO; use Ada.Text_IO;
with Aircraft_Pkg; use Aircraft_Pkg;
with Fighter_Aircraft_Pkg; use Fighter_Aircraft_Pkg;

package Stealth_Fighter_Pkg is
    type Stealth_Fighter is new Fighter_Aircraft with record
        Stealth_Capability : Boolean := False;
    end record;

    -- Method to enable or disable stealth
    procedure Set_Stealth_Mode (S : in out Stealth_Fighter; Enable : Boolean);

    -- Overriding the Fly procedure
    overriding
    procedure Fly (S : in out Stealth_Fighter; Alt_Increment : Integer; Speed_Increment : Integer);
end Stealth_Fighter_Pkg;

package body Stealth_Fighter_Pkg is
    procedure Set_Stealth_Mode (S : in out Stealth_Fighter; Enable : Boolean) is
    begin
        S.Stealth_Capability := Enable;
    end Set_Stealth_Mode;

    procedure Fly (S : in out Stealth_Fighter; Alt_Increment : Integer; Speed_Increment : Integer) is
    begin
        S.Altitude := S.Altitude + Alt_Increment;
        S.Speed := S.Speed + Speed_Increment;
        if S.Stealth_Capability then
            Put_Line (S.Model & " is flying stealthily at " & Integer'Image(S.Speed) & " mph, Altitude: " & Integer'Image(S.Altitude) & " feet.");
        else
            Put_Line (S.Model & " is flying at " & Integer'Image(S.Speed) & " mph, Altitude: " & Integer'Image(S.Altitude) & " feet.");
        end if;
    end Fly;
end Stealth_Fighter_Pkg;

We have created the Stealth_Fighter type as an extension of the Fighter_Aircraft type, adding a Stealth_Capability feature. We also provide a Set_Stealth_Mode method to change the stealth capability and override the Fly method to include stealth capability in the behavior.

Now let’s create a Stealth_Fighter object and use it:

with Ada.Text_IO; use Ada.Text_IO;
with Aircraft_Pkg; use Aircraft_Pkg;
with Fighter_Aircraft_Pkg; use Fighter_Aircraft_Pkg;
with Stealth_Fighter_Pkg; use Stealth_Fighter_Pkg;

procedure Main is
    S : Stealth_Fighter := (Model => "F-35", Weapon_System => "AIM-120 AMRAAM", Altitude => 0, Speed => 0, Stealth_Capability => False);
begin
    S.Fly(15000, 1200); -- Fly with 15000 feet altitude increment and 1200 mph speed increment
    S.Set_Stealth_Mode(True);
    S.Fly(5000, 600); -- Fly with 5000 feet altitude increment and 600 mph speed increment
end Main;

In this main procedure, we create a Stealth_Fighter object S and make it fly twice: once without stealth mode and once with stealth mode. The output would be:

“F-35 is flying at 1200 mph, Altitude: 15000 feet.”

“F-35 is flying stealthily at 600 mph, Altitude: 20000 feet.”

This detailed example illustrates the use of inheritance in Ada with a multi-level hierarchy (Aircraft -> Fighter_Aircraft -> Stealth_Fighter). We can see how each derived type extends the base type with additional features and behavior modifications.

Polymorphism Example

In the context of an aerospace flight management software, let’s consider two kinds of aircraft: Passenger_Aircraft and Cargo_Aircraft. Both types of aircraft share common features like altitude and speed, but they have different behaviors when it comes to load management due to differing passenger and cargo capacities.

Firstly, we’ll define an abstract Aircraft type which will serve as our base class. All types of aircraft will inherit from this class and override its Manage_Load procedure to implement their specific load management strategies:

with Ada.Text_IO; use Ada.Text_IO;

package Aircraft_Pkg is
    type Aircraft is abstract tagged record
        Model : String(1..20);
        Altitude : Integer := 0;
        Speed : Integer := 0;
    end record;

    procedure Manage_Load (A : in out Aircraft) is abstract;
    procedure Fly (A : in out Aircraft; Alt_Increment : Integer; Speed_Increment : Integer);
end Aircraft_Pkg;

package body Aircraft_Pkg is
    procedure Fly (A : in out Aircraft; Alt_Increment : Integer; Speed_Increment : Integer) is
    begin
        A.Altitude := A.Altitude + Alt_Increment;
        A.Speed := A.Speed + Speed_Increment;
        Put_Line (A.Model & " is flying at " & Integer'Image(A.Speed) & " mph, Altitude: " & Integer'Image(A.Altitude) & " feet.");
    end Fly;
end Aircraft_Pkg;

Next, we define the Passenger_Aircraft and Cargo_Aircraft types, each with their specific implementation of the Manage_Load procedure:

with Ada.Text_IO; use Ada.Text_IO;
with Aircraft_Pkg; use Aircraft_Pkg;

package Passenger_Aircraft_Pkg is
    type Passenger_Aircraft is new Aircraft with record
        Passenger_Count : Integer := 0;
    end record;

    overriding
    procedure Manage_Load (P : in out Passenger_Aircraft);
end Passenger_Aircraft_Pkg;

package body Passenger_Aircraft_Pkg is
    procedure Manage_Load (P : in out Passenger_Aircraft) is
    begin
        Put_Line (P.Model & " is managing load for " & Integer'Image(P.Passenger_Count) & " passengers.");
    end Manage_Load;
end Passenger_Aircraft_Pkg;

package Cargo_Aircraft_Pkg is
    type Cargo_Aircraft is new Aircraft with record
        Cargo_Weight : Float := 0.0;
    end record;

    overriding
    procedure Manage_Load (C : in out Cargo_Aircraft);
end Cargo_Aircraft_Pkg;

package body Cargo_Aircraft_Pkg is
    procedure Manage_Load (C : in out Cargo_Aircraft) is
    begin
        Put_Line (C.Model & " is managing load for " & Float'Image(C.Cargo_Weight) & " tons of cargo.");
    end Manage_Load;
end Cargo_Aircraft_Pkg;

Finally, we will demonstrate polymorphism by defining a procedure Prepare_For_Flight that accepts an Aircraft and calls its Manage_Load and Fly methods. It will correctly prepare for flight, whether the aircraft is a Passenger_Aircraft or a Cargo_Aircraft:

with Ada.Text_IO; use Ada.Text_IO;
with Aircraft_Pkg; use Aircraft_Pkg;
with Passenger_Aircraft_Pkg; use Passenger_Aircraft_Pkg;
with Cargo_Aircraft_Pkg; use Cargo_Aircraft_Pkg;

procedure Main is
    P : Passenger_Aircraft_Pkg.Passenger_Aircraft := (Model => "Boeing 747", Altitude => 0, Speed => 0, Passenger_Count => 150);
    C : Cargo_Aircraft_Pkg.Cargo_Aircraft := (Model => "C-130 Hercules", Altitude => 0, Speed => 0, Cargo_Weight => 20.0);

    procedure Prepare_For_Flight (A : in out Aircraft_Pkg.Aircraft'Class) is
    begin
        A.Manage_Load;
        A.Fly(10000, 600);
    end Prepare_For_Flight;
begin
    Prepare_For_Flight(P);
    Prepare_For_Flight(C);
end Main;

This example shows how polymorphism works in Ada and how it can be leveraged in a flight management software scenario. By using an abstract base class and tagging, we can ensure that the correct Manage_Load procedure is called for each specific type of aircraft.

Exception Handling

Exception handling is a crucial feature of any programming language. It is used to handle runtime errors that can potentially cause a program to halt unexpectedly. Ada has a robust exception handling mechanism that provides a clear syntax for dealing with exceptions that occur during program execution.

Ada has several predefined exceptions that are raised by language-defined checks. These include Constraint_Error, Program_Error, Storage_Error, and Tasking_Error, among others. Developers can also define their own exceptions when needed.

Let’s discuss how exception handling in Ada works, using a hypothetical scenario in aerospace software development: An attempt to divide by zero when calculating the aspect ratio of an aircraft wing (which could be a potential source of a Constraint_Error).

An Ada exception handling block generally consists of a begin statement, followed by a sequence of statements, an exception keyword, and one or more when clauses specifying the exceptions to be handled. The structure looks something like this:

begin
   -- normal sequence of statements here
exception
   when EXCEPTION_NAME =>
      -- sequence of statements to execute when EXCEPTION_NAME occurs
end;

Let’s illustrate this with an example:

with Ada.Text_IO; use Ada.Text_IO;

procedure Calculate_Aspect_Ratio is
   Wing_Span : Float := 35.0;  -- in meters
   Wing_Width : Float := 0.0;  -- in meters
   Aspect_Ratio : Float;
begin
   -- Attempt to calculate the aspect ratio of the aircraft wing
   Aspect_Ratio := Wing_Span / Wing_Width;
   Put_Line("Aspect ratio: " & Float'Image(Aspect_Ratio));
exception
   when CONSTRAINT_ERROR =>
      -- Handle division by zero error
      Put_Line("Error: Wing_Width must not be zero.");
end Calculate_Aspect_Ratio;

In the above example, if Wing_Width is zero, the division operation will raise a Constraint_Error exception. The when CONSTRAINT_ERROR => block will catch this exception and execute the corresponding exception handling code, preventing the program from halting abruptly and providing a clear error message to the user.

This is a simple example. In a real-world aerospace application, the exception handling code might involve more complex actions like logging the error, notifying other parts of the system, or performing fallback operations. The key point is that Ada’s exception handling mechanism provides a structured way to handle runtime errors and keep the system functioning in a controlled manner even when things go wrong.

Exception Handling Example

Let’s consider a scenario in an aerospace software system where the altitude and speed of an aircraft are monitored and controlled. There are safety thresholds for both values: if the altitude exceeds 60,000 feet, a High_Altitude exception is raised, and if the speed exceeds Mach 2.5 (approximately 1915 mph), a High_Speed exception is raised.

Let’s define these exceptions, and create a Monitor_And_Control procedure that takes the current altitude and speed, checks whether they are within the safety limits, and adjusts them if necessary:

with Ada.Text_IO; use Ada.Text_IO;

procedure Aircraft_Monitoring is
    High_Altitude : exception;
    High_Speed : exception;

    procedure Monitor_And_Control (Altitude : in out Integer; Speed : in out Integer) is
        Maximum_Altitude : constant Integer := 60000;  -- in feet
        Maximum_Speed : constant Integer := 1915;  -- in mph (approximately Mach 2.5)
    begin
        if Altitude > Maximum_Altitude then
            raise High_Altitude;
        elsif Speed > Maximum_Speed then
            raise High_Speed;
        else
            Put_Line("Altitude: " & Integer'Image(Altitude) & " feet, Speed: " & Integer'Image(Speed) & " mph. Everything is within limits.");
        end if;
    exception
        when High_Altitude =>
            Put_Line("Warning: High Altitude! Decreasing altitude...");
            Altitude := Maximum_Altitude;
        when High_Speed =>
            Put_Line("Warning: High Speed! Decreasing speed...");
            Speed := Maximum_Speed;
    end Monitor_And_Control;

    Altitude : Integer := 62000;
    Speed : Integer := 2000;
begin
    Monitor_And_Control(Altitude, Speed);
    Monitor_And_Control(Altitude, Speed);
end Aircraft_Monitoring;

This program first calls Monitor_And_Control with an altitude of 62,000 feet and a speed of 2,000 mph, both exceeding the safety limits. The procedure raises and handles the High_Altitude exception first, reducing the altitude to 60,000 feet. The procedure is then called again, this time raising and handling the High_Speed exception and reducing the speed to 1915 mph. Finally, Monitor_And_Control is called one more time, and since the altitude and speed are now within the safety limits, it simply reports the current values without raising any exceptions.

This demonstrates Ada’s exception handling mechanism in a context relevant to aerospace software. By defining and raising exceptions when necessary, we can ensure that the aircraft’s altitude and speed are monitored and controlled effectively. Exception handling allows the program to respond to error conditions and prevent them from leading to serious issues or system crashes.

Chapter 5: Object-Oriented Programming in ADA Programming Language
Scroll to top
error: Content is protected !!