Previous |
Contents |
Next |
A journey of a thousand miles
must begin with a single step.
Proverb
In this book youre going to learn how to write programs in Ada, a general purpose programming language originally commissioned by the US Department of Defense. The first standard for the Ada language was finalised in 1983 and was later revised to produce a new updated standard in 1995. The earlier version is now known as Ada 83 and the later version (the one covered in this book) as Ada 95. Ada programs use a somewhat stilted kind of formal English in order to specify the operations you want the computer to perform. You provide some declarations which specify the objects that the program is going to deal with. You also provide a sequence of statements specifying the actions you want performed on those objects and the computer will perform them in order, one after the other. A set of declarations together with a sequence of statements like this makes up a procedure which you invent a name for. For example, you might write a procedure which clears the screen and call it Clear_Screen.
Computers do not understand Ada directly; the text of your procedure (the source code) must first of all be translated (compiled) into an internal form by an Ada compiler. The compiler protects you against your own stupidity by detecting a lot of common errors such as those which arise from typing mistakes, and these must be corrected before you can go any further. Once a procedure has been compiled successfully it is added to your program library. Whenever you want to perform that sequence of statements you can just refer to the procedure by the name youve given it. For example, you might write another procedure which needs to clear the screen before displaying something on it. All it would have to do would be to call on the services of the existing Clear_Screen procedure. This means that you dont have to rewrite things youve already written, which reduces the amount of work you have to do both in writing the programs and in testing them to make sure they work properly. You can also make use of procedures written by other people without having to know all the details of how they work.
The next step for turning your procedure from a library unit into a working program is to link it (also referred to as building or binding it). This combines it with any other library units it refers to. In addition to any library units you may have written, the Ada language specification defines a number of standard library units that all Ada systems must provide (e.g. operations like input and output) and it is quite likely that your procedure will have used one or more of these, either directly or indirectly. The linker takes your procedure, any library units it refers to, any others that they refer to and so on, and binds them all together into a single executable program which you can then run.
Of course it is quite possible (almost inevitable, as you will discover!) that the program wont work the way you expected; the compiler isnt omniscient, so the fact that your program compiled successfully just means that its a valid Ada program. It doesnt guarantee that its the particular Ada program you expected it to be, and its up to you to test it thoroughly to make sure it behaves the way you intended it to. You might hope for a program which displays a five times table but end up with a program that displays the number 5 over and over again, in which case its back to the drawing board: youll need to track down the errors (known as bugs in the trade), correct them, recompile, relink, and try again.
Writing a program which produces the right answers from sensible input is only part of the story. You also need to make sure that it still behaves sensibly with nonsensical input. For example, the program might expect you to type in a number; what happens if you type in your name instead? If the program responds with an error message, ignores the erroneous input and carries on working, fine. If the program crashes (i.e. halts unexpectedly), this is not so good. You may never get the result that the program is supposed to produce. Even worse, the program might get stuck in an infinite loop where it just ends up doing the same thing over and over again until it is forcibly halted.
Obviously readability is a major factor in determining how easy it is to maintain a program. You need to be able to read and understand what the program does in order to change it. As I said earlier, Ada programs are written using a somewhat stilted formal English. Writing a program is in many ways just like writing an essay. It can be well- presented and well-structured or it can be a tangled rambling mess. If you want other people to be able to understand what its all about (or even yourself in six months time!) you need to make an effort to present the program so that its structure and meaning is easy to understand. If you dont, youll end up thinking what on earth does this bit do? and wasting a lot of time trying to understand whats going on.
Maintainability is a measure of how easy it is to change a program. Ideally a single change should involve changing just one part of the program. If you have to alter several parts of the program to effect the change theres a risk that youll forget to change some of them or that youll make a mistake in some places but not in others. For example, consider a payroll program that can handle up to 100 employees. If the number of employees expands, the program will need changing to handle (say) 200 employees. If there is a single line in the program that says that the number of employees is 100 and the rest of the program refers to the number of employees rather than using the number 100 then there will be no problem. If, however, the value 100 appears as a magic number throughout the program, every single occurrence of 100 will need changing to 200. The chances of missing one are fairly high. Worse, some places where 100 is used might be involved in calculating percentages, or it might refer to the number of pennies in a pound, and we might accidentally end up with 200 pence to the pound or percentages calculated wrongly. And what about the places where the magic number 99 appears? Should 99 be changed to 199 throughout, or rather, to the number of employees - 1?
Another situation that arises quite often is the need to make the program work on several different systems. A program should ideally be portable from one system to another; all you do is recompile it and, hey presto, it works! Unfortunately life is rarely that simple; there are usually differences from one system to another such as the size of the largest possible number that can be handled or the capabilities of the display screen. Although you can avoid assuming that your program is running on a system which has particular characteristics it will sometimes be impossible to eliminate system-specific dependencies. About the only thing youll be able to to do in such situations is to gather together all the system-specific parts of the program so that its easy to locate them and change them.
After writing a few programs you normally discover that a lot of the time youre doing the same old thing that you did before, at least in places. Input and output; sorting collections of data into order; trying to find things in a collection; there are dozens of common features shared by different programs. After a while a sensation of d‚j… vu comes upon you and you realise youve done it all before. The second time you do it better and/or faster than the first time. The third time you do it better and/or faster than the second. The fourth time you start yawning. How easy is it to reuse what youve already written and rewritten? Ada provides some mechanisms which allow you to write software which is flexible enough that you can reuse it fairly easily in a variety of different situations. Learning to make the most of these mechanisms is always useful but it becomes a necessity as soon as programs start to go above a few thousand lines of code in size.
This separation also enhances maintainability and portability. As long as all you rely on when you use something is a specification of what it does, it is easy to change how it does it. This can be done behind the scenes with no visible effects other than the need to take any existing programs which rely on the specification and relink them so that they use the new implementation. Portability is also aided by the fact that you can say this procedure clears the screen as a specification but then provide different implementations for different systems with different types of screen.
What you end up with is an abstract data type. Abstract data types provide no information about their internal implementation; instead, all you know is the set of permissible values of the type and the operations that can be performed on them. This is just like the way that numbers are handled in a computer. You dont usually have access to the internal representation of numbers in the computers memory; all you know about is the visible representation of numbers like 3.1416 and the fact that you can perform operations like addition and subtraction on them. Ada packages provide the mechanism by which programmers can define their own abstract data types.
Ada allows you to separate algorithms from the data they deal with by allowing you to write generic library units. For example, in order to sort a collection of data all you really need to know is how to compare data items with each other to discover if they need reordering and how to move them to the correct position in the collection if they are out of order. The same algorithm can be used regardless of whether youre sorting numbers or names as long as these conditions are met. Ada allows you to write a generic sorting procedure which, given a data type which satisfies the requirements of the algorithm, will sort a collection of items of that type. All you have to do to be able to use it to sort items of a particular type is to specify the item type to be used and the method for comparing two items. You can use the same algorithm over and over again for different types of data without the need to modify it in any way.
A related problem is dealing with those situations where the different types of account behave differently. Normally this involves checking the type of account at each point in the program where different behaviour is possible and then choosing the behaviour appropriate to that account type. A new account type will therefore involve a number of modifications at different places in the program with the associated risk of missing a change or changing something incorrectly. Inheritance guarantees that all bank accounts will share certain common characteristics such as the ability to deposit and withdraw money. New types of bank account will either inherit the existing behaviour or provide a replacement for it. In either case it will still be possible to perform those operations. Polymorphism reflects the ability to provide different implementations of the same operation for different types and to select the correct behaviour automatically for the actual type of object being dealt with. Rather than seeing a program as a set of procedures which manipulate data, you can look at it as a set of objects which you can ask to perform particular operations. Each object responds to a request to perform a particular operation by executing the version of the operation which is appropriate for it. You dont have to check what type of account youre dealing with when you say deposit some money; instead, you tell the account that you want to deposit some money and it does it in the way thats appropriate for the type of account that it is. As a result, you wont need to change any existing code when you introduce a new type of account.
Previous |
Contents |
Next |
This file is part of
Ada 95: The Craft of Object-Oriented Programming
by John English.
Copyright © John
English 2000. All rights reserved.
Permission is given to redistribute this work for non-profit educational
use only, provided that all the constituent files are distributed without
change.