Referencing and de-referencing objects in an Object Oriented Environment can be a challenge. These are real life examples; the names of the objects have been changed to preserve the identity of the victims. Please fasten your seat belt.
When I take an airplane, I cannot help but think of all the poor programming practices I have witnessed –some committed by myself– over the years. I often wonder if the on-board computers have been programmed by my earlier engineering peers.
Architectural Concerns
The present article is about poor coding practices resulting from poor risk analysis, poor critical thinking, all culminating in poor implementation. You cannot build resilient software if you foundations are weak, whether from the tools you use or how you use them. Let’s approach robust coding with my favorite quote from an engineer with whom I do not share the view. I could see the benefits and even possibly some logic behind it, but this is why I hate to fly:
If a bug cannot occur, it is not a bug.
W.W.(ref.)
I guess that I am not inclined to take the risk; a condition could have been overlooked, or simply created at a later maintenance stage. New situations will arise as the software evolves, and code that was once safe may be misunderstood and misused.
From the same engineer, I’ve also heard «Well. Of course it will crash if you pass it corrupted data…» when talking about one of the drawing API.
Crash on landingGear.deploy()
Overview
1. A short Object Oriented Programming Refresher
1.1 Object Inheritance
Say you have three classes. An airplane, a commercial airliner and a private jet. We can all agree that both the jet and the airliner are kinds of airplanes.
1.2. Object Properties
Say that an airplane has landing gear, I reckon all airplanes have landing gear. Assuming we are not discussing here the very specific case of hydroplanes, the gear (at least a generic one) should probably belong to the airplane object.
1.3. Inherited Properties
So in the real world, having landing gear is not particularly specific to the private jet or the commercial airliner. Being fitted with landing gear, retractable or not, is general to all airplanes.
1.4. Class Members
The landing gear, in itself, can be a pretty complex device.
-
Member variables
It has members that remember states, such as stowed away vs. ready for landing.
Example: isReady -
Member methods
It also has functions, which generally, but not always, will change its state. Such functions can be deploy vs. stow.
Example: deploy()
This is where encapsulating functionality into an object starts to make a lot of sense. You need not to know the specifics of how a mechanism (or an algorithm) is implemented. You only need to know how to invoke it: in this example, it’s Deploy().
1.5. Delegate Behavior
In this simplified model, it matters not which kind of airplane you fly. All you need to do is to flip a switch to initiate the landing procedure, and the airplane object just “knows” what to do.
1.6. Larger Picture
2. A Programming Error Crash Course: Dont’ fly with those!
I have gathered below a few errors I have seen in the past. Do not try this at home!
2.1. public static
BAD:
// Whenever needed if( nil == Airplane.staticLandingGear) { Airplane.staticLandingGear = create an instance of LandingGear } // ... followed somewhere upon destruction of the object: // Upon destruction of the creator if( nil != Airplane.staticLandingGear) { delete Airplane.staticLandingGear Airplane.staticLandingGear = nil }
While this may look proper at first glance, and possibly even work depending on the situation, I have seen this code create havoc when placed in the “Private Jet” or “Commercial Airliner” sub-class. Create more than one plane and you have a disaster.
BETTER:
// Whenever needed aLandingGear = Airplane.getLandingGear() // ...and the LandingGear instance in turn created and maintained by a singleton getLandingGear() { // Ensures only one LandingGear instance exists if( nil == staticLandingGear) { staticLandingGear = create a unique instance of LandingGear } return staticLandingGear }
- Problem: More than one object may destroy the public static variable
- Suggestion: Use a singleton and a private static.
2.2. silent error
BAD:
if( nil != aLandingGear) { aLandingGear.deploy() }
Defensive programming has it’s price. In this case, the software will not crash if the LandingGear object does not exist. Good.
Conversely, the plane will crash because no landing gear has been deployed. Bad.
This situation could have been detected early on if the software had not failed silently during the development phase, before it was deployed.
BETTER:
assert( aLandingGear); // Will alert QA that something went terribly wrong if( nil != aLandingGear) { aLandingGear.deploy() } else { // Something is terribly wrong and we should not be there engageRecoveryProcedure }
- Problem:Failure goes undetected and unreported during development
- Suggestion:Use assert() and error_log() to report unexpected conditions
2.3. uninitialized object
For this example, assume that the landing gear needs to be connected to an airplane.
BAD:
if( ! aLandingGear) { aLandingGear = new LandingGear() } // The app will not crash, because aLandingGear exists aLandingGear->deploy() // Will do nothing. Plane will crash!
If an object MUST be initialized, one possibility is to make the default constructor private, forcing the engineer to use a designated initializer.
BETTER:
if( ! aLandingGear) { aLandingGear = new LandingGear( anAirplane) }
- Problem:Failure to initialize an object
- Suggestion:Make constructor private to force the use of proper initializer, otherwise source code will not compile in the first place.
UML References
- https://yuml.me/diagram/scruffy;scale:200/class/[Airplane].
- Format described at yuml.me