Complete Aircraft Engine Simulation Code Listing


Inheritance Example: Aircraft Engine Simulation

As the television chef Emeril Lagasse would say, “Bam! — kick it up a notch!”. This example expands on the aircraft engine simulation originally presented in chapter 10. Here the concepts of inheritance are fused with compositional design to yield a truly powerful combination.

In this example you will also be introduced to the concept of a listener. A listener is a component that responds to events. Listeners are used extensively in the AWT and Swing packages to respond to different GUI events. (You will be formally introduced to GUI programming in chapter 12.) This example also utilizes the Vector and Hashtable collection classes from the java.util package and the synchronized keyword which is used in several methods in the Part class to ensure atomic access to certain objects by client methods. The collection classes are discussed in detail in chapter 17 and the synchronized keyword is discussed in detail in chapter 16.

Aircraft Engine Simulation UML Diagram

Let’s start by discussing the UML diagram for the engine simulation shown in figure 11-21. As you can see from the class diagram this version of the aircraft engine simulation contains decidedly more classes than its chapter 10 cousin. The primary class in the inheritance hierarchy is the Part class. Essentially, every class is a Part with the exception of the PartStatus and PartEvent classes. Part implicitly inherits from the java.lang.Object class but this relationship is not shown on the diagram. The concept of a Part encompasses individual as well as composite parts. The Part class provides the general functionality for both single as well as composite part objects.

image from book
Figure 11-21: UML Class Diagram For Aircraft Engine Simulator

Two other Java API classes that are shown in the class diagram include EventObject and EventListener. The PartEvent class inherits from EventObject and the PartListener interface inherits from the EventListener interface.

The interfaces IPump, ISensor, and IEngine specify the methods that must be implemented by parts of these types. The classes Pump, Sensor, and Engine inherit the basic functionality provided by the Part class and implement the appropriate interface.

The FuelPump and OxygenSensor classes provide concrete implementations for the Pump and Sensor classes respectively. The SimpleEngine class extends Engine and implements an interface named PartListener. Note here that although SimpleEngine implements the PartListener interface, other parts could implement the PartListener interface as required.

It should also be noted that this program is incomplete at this stage. I have only provided implementations for three concrete classes: FuelPump, OxygenSensor, and SimpleEngine. You will have the opportunity to add parts and functionality to this program in one of the Suggested Projects found at the end of this chapter.

Simulation Operational Description

The Part class provides the general functionality for all Part objects. A Part object can be a single, atomic part or it can be an aggregate of several Part objects. The Parts comprising a composite Part object are stored in a Part’s _sub_parts Hashtable. Part objects are added to the _sub_parts Hashtable by calling the addSubPart() method.

A particular subclass of Part may be a PartListener if it implements the PartListener interface. The PartListener interface specifies three methods that correspond to state changes on Sensors, Pumps, and the working status of a part. A Part that is also a PartListener will need to register itself with the Part of interest. Take for example the SimpleEngine class. Referring to the code listing given in example 11.35 — on lines 9 and 10 in the constructor, the SimpleEngine class creates two subparts and adds them to the _sub_parts Hashtable by calling the addSubPart() method. On lines 11 and 12 it then gets a reference to each of its subparts by calling the getSubPart() method and registering itself as a PartListener for each of its subparts.

Example 11.35: SimpleEngine.java

image from book
 1     public class SimpleEngine extends Engine implements PartListener { 2 3 4       private String[] part_names = {"FuelPump 1", "Oxygen Sensor 1"}; 5 6       public SimpleEngine(PartStatus status, String name){ 7        super(status, name); 8 9        addSubPart(part_names[0], new FuelPump(part_names[0])); 10       addSubPart(part_names[1], new OxygenSensor(part_names[1])); 11       getSubPart(part_names[0]).addPartListener(this); 12       getSubPart(part_names[1]).addPartListener(this); 13       System.out.println("SimpleEngine object created!"); 14       } 15 16      public SimpleEngine(String name){ 17       super(name); 18 19        addSubPart(part_names[0], new FuelPump(part_names[0])); 20        addSubPart(part_names[1], new OxygenSensor(part_names[1])); 21        getSubPart(part_names[0]).addPartListener(this); 22        getSubPart(part_names[1]).addPartListener(this); 23        System.out.println("SimpleEngine object created!"); 24        } 25 26        /****************************************** 27          IEngine Interface Method Implementations 28        *******************************************/ 29       public void start(){ 30           if(isWorking()){ 31           ((Pump) getSubPart(part_names[0])).setSpeed(100); 32           } 33       } 34 35 36       public void stop(){ 37         ((Pump) getSubPart(part_names[0])).setSpeed(0); 38       } 39 40       public void incrementThrust(){ 41           ((Pump) getSubPart(part_names[0])).incrementSpeed(25); 42       } 43 44 45      public void decrementThrust(){ 46             ((Pump) getSubPart(part_names[0])).incrementSpeed(-25); 47         } 48 49 50        /********************************** 51           PartListener Interface Methods 52         *********************************/ 53 54        public void sensorReadingChangePerformed(PartEvent pe){ 55          System.out.println("SimpleEngine: sensorReadingChangePerformed() method called!"); 56          System.out.println(pe.getPartName() + " reading changed to " + pe.getValue()); 57 58         } 59 60        public void pumpSpeedChangePerformed(PartEvent pe){ 61          System.out.println("SimpleEngine: pumpSpeedChangePerformed() method called!"); 62          System.out.println(pe.getPartName() + " speed changed to " + pe.getValue()); 63         } 64 65        public void partStatusChangePerformed(PartEvent pe){ 66          System.out.println("SimpleEngine: partStatusChangePerformed() method called!"); 67          System.out.println(pe.getPartName() + pe.statusToString()); 68         } 69 70      }
image from book

To see how the PartListener mechanism works let’s step through a pump speed change. In the start() method of the SimpleEngine class the speed of its FuelPump subpart is set to 100. (see line 31 of example 11.35) The setSpeed() method is called on a Pump interface but, because the FuelPump class overrides Pump’s setSpeed() method, it is the FuelPump’s version of setSpeed() that gets called. Now, go to the FuelPump class listed in example 11.33 and find the setSpeed() method. If the speed desired falls within the valid range of a FuelPump object then the Pump version of setSpeed() is called via the super object. Go now to the Pump class listed in example 11.30 and find its version of the setSpeed() method. It’s sole job is to call the firePumpSpeedChangeEvent() method with the indicated speed as an argument. The firePumpSpeedChangeEvent() method is located in the Part class so go now to example 11.25 and find it.

Example 11.30: Pump.java

image from book
 1     public abstract class Pump extends Part implements IPump { 2 3      public Pump(PartStatus status, String name){ 4         super(status, name); 5         System.out.println("Pump object created!"); 6       } 7 8      public Pump(String name){ 9         super(name); 10        System.out.println("Pump object created!"); 11      } 12 13     public void setSpeed(int speed){ 14         firePumpSpeedChangeEvent(speed); 15       } 16 17     public void incrementSpeed(int increment){ 18        firePumpSpeedChangeEvent(getSpeed()); 19      } 20 21     public int getSpeed() { return 0; } 22 23    }
image from book

Example 11.33: FuelPump.java

image from book
 1     public class FuelPump extends Pump{ 2 3      private static final int MIN_SPEED = 0; 4      private static final int MAX_SPEED = 1000; 5 6      private int _speed = 0; 7 8      public FuelPump(PartStatus status, String name){ 9         super(status, name); 10        System.out.println("Fuel Pump " + name + " created!"); 11      } 12 13     public FuelPump(String name){ 14        super(name); 15        System.out.println("Fuel Pump " + name + " created!"); 16       } 17 18     public void setSpeed(int speed){ 19        if((speed >= MIN_SPEED) && (speed <= MAX_SPEED)){ 20            _speed = speed; 21            super.setSpeed(speed); 22         }else{ 23              System.out.println("Pump speed cannot exceed specified range:" + 24                              MIN_SPEED + " - " + MAX_SPEED); 25          } 26     } 27 28     public void incrementSpeed(int i){ 29      if(((_speed + i) >= MIN_SPEED) &&   ((_speed + i) <= MAX_SPEED)){ 30          _speed += i; 31          super.incrementSpeed(i); 32       } else { 33           System.out.println("Pump speed cannot exceed specified range: " + MIN_SPEED + " - " + MAX_SPEED); 34         } 35      } 36 37     public int getSpeed() { return _speed; } 38 39    }
image from book

Example 11.25: Part.java

image from book
 1     import java.util.*; 2 3     public abstract class Part { 4 5       /********************************* 6         private attributes 7       *********************************/ 8       private PartStatus _status = null; 9       private String _part_name = null; 10       private Vector _part_listeners = null; 11       private Hashtable _sub_parts = null; 12 13 14       /********************************* 15          constructor 16       **********************************/ 17       public Part(PartStatus status, String part_name){ 18         _status = status; 19         _part_name = part_name; 20        System.out.println("Part object created!"); 21       } 22 23       /********************************* 24          default constructor 25       **********************************/ 26       public Part(String part_name){ 27          _status = PartStatus.WORKING; 28          _part_name = part_name; 29          System.out.println("Part object created!"); 30       } 31 32       /********************************* 33          public interface methods 34       **********************************/ 35 36       public PartStatus getPartStatus(){ return _status; } 37 38       public String  getPartName(){ return _part_name; } 39 40       public void setPartStatus(PartStatus status){ 41        _status = status; 42        firePartStatusChangeEvent(); 43       } 44 45       public boolean isWorking(){ 46        if(_sub_parts != null){    // there are subparts 47          for(Enumeration e = getSubPartsEnumeration(); e.hasMoreElements();){ 48             if(((Part) e.nextElement()).isWorking()) { 49                continue; //great! 50                }else{ 51                   setPartStatus(PartStatus.NOT_WORKING); 52                   break; 53                  } 54          } 55        } 56        System.out.println(getPartName() + " " + " isWorking status = " + _status.isWorking()); 57        return _status.isWorking(); 58       } 59 60       public void addPartListener(PartListener pl){ 61        if(_part_listeners == null){ 62            _part_listeners = new Vector(); 63            _part_listeners.add(pl); 64         }else { 65            _part_listeners.add(pl); 66           } 67       } 68 69       public void removePartListener(PartListener pl){ 70         if(_part_listeners == null){ 71               ; //do nothing 72            } else { 73              if(_part_listeners.isEmpty()){ 74                  ; // do nothing 75                 } else { 76                     _part_listeners.removeElement(pl); 77                    } 78         } 79       } 80 81       public void enable(){ 82         setPartStatus(PartStatus.WORKING); 83       } 84 85       public void disable(){ 86         setPartStatus(PartStatus.NOT_WORKING); 87       } 88 89 90       public void fireSensorReadingChangeEvent(double reading){ 91        if(_part_listeners.isEmpty()){ 92              ;  //there's nothing to do! 93           } else { 94              Vector listeners_copy = null; 95              synchronized(this){ 96                   listeners_copy = (Vector)_part_listeners.clone(); 97                 } 98              PartEvent pe = new PartEvent(this, _part_name, reading); 99              for(Enumeration e = listeners_copy.elements(); e.hasMoreElements();){ 100               ((PartListener) (e.nextElement())).sensorReadingChangePerformed(pe); 101              } 102            } 103     } 104 105      public void firePumpSpeedChangeEvent(double speed){ 106       if(_part_listeners.isEmpty()){ 107             ;  //there's nothing to do! 108          } else { 109             Vector listeners_copy = null; 110             synchronized(this){ 111                  listeners_copy = (Vector)_part_listeners.clone(); 112                } 113             PartEvent pe = new PartEvent(this, _part_name, speed); 114             for(Enumeration e = listeners_copy.elements(); e.hasMoreElements();){ 115               ((PartListener) (e.nextElement())).pumpSpeedChangePerformed(pe); 116              } 117            } 118     } 119 120     public void firePartStatusChangeEvent(){ 121       if(_part_listeners.isEmpty()){ 122             ;  //there's nothing to do! 123          } else { 124             Vector listeners_copy = null; 125             synchronized(this){ 126                  listeners_copy = (Vector)_part_listeners.clone(); 127               } 128             PartEvent pe = new PartEvent(this, _part_name); 129             for(Enumeration e = listeners_copy.elements(); e.hasMoreElements();){ 130               ((PartListener) (e.nextElement())).partStatusChangePerformed(pe); 131              } 132            } 133     } 134 135      public void addSubPart(String name, Part part){ 136       if(_sub_parts == null){ 137         _sub_parts = new Hashtable(); 138         _sub_parts.put(name, part); 139       }else{ 140           _sub_parts.put(name, part); 141         } 142     } 143 144    public Part getSubPart(String name){ 145      assert (_sub_parts != null);  //can't call if no subparts exist! 146      return (Part)_sub_parts.get(name); 147    } 148 149     public Enumeration getSubPartsEnumeration(){ 150       assert (_sub_parts != null ); 151       return _sub_parts.elements(); 152    } 153  }
image from book

The firePumpSpeedChangeEvent() does several things. First, it checks to see if its _part_listeners Vector is empty. If it is then it doesn’t have any interested PartListeners. If it has one or more interested PartListeners it clones its _part_listeners Vector object in a synchronized block to prevent sporadic behavior, creates a new PartEvent object, converts the listeners_copy vector to an Enumeration, and then steps through each element of the Enumeration calling the pumpSpeedChangePerformed() method on each interested PartListener object. This is when the pumpSpeedChangePerformed() method is called on the SimpleEngine object resulting in the console output you see when you run the program.

Take A Deep Breath And Relax!

At this point, since you have yet to be formally introduced to them, some of the concepts regarding the use of Vectors, Hashtables, and Enumerations will seem cryptic. The PartListener firing mechanism is also difficult at first to understand. If you don’t fully grasp the complete workings of this complex example right now don’t worry. My intent is to challenge you to dive into the code and trace the execution. That’s how you learn how to program!

Compiling The Aircraft Engine Simulation Code

The easiest way to compile all the aircraft engine simulation code is to put the source files in one directory and issue the following command:

 javac -source 1.4 *.java

You need to add the -source 1.4 option because of the use of the assert keyword in the Part class.




Java For Artists(c) The Art, Philosophy, and Science of Object-Oriented Programming
Java For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504052
EAN: 2147483647
Year: 2007
Pages: 452

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net