Introduction to Programming 2
Brief Summary Notes by Steve Benford
PR1 taught you how to write relatively small-scale computer programs to solve a variety of problems. As a result, you should have developed some basic problem solving skills as well as a working knowledge of C++.
So far, you have programmed on your own, producing one-off "stand-alone" programs. This is a far cry from real-world programming which often involves teams of programmers producing massive and complex systems over a long period of time. PR2 changes our focus away from the microscopic nuts-and-bolts level of small-scale programming towards programming in the large - i.e. solving large problems as part of a programming team.
As with PR1 the course will cover general programming concepts. It will also introduce new features of C++ to support these concepts. Regular coursework will be a central part of the course. More specific goals include:
As you have probably already noticed, the hardest part of programming is often problem-solving, not writing code. This course will attempt to further develop problem solving as well as programming skills. We will also introduce some "software tools" which support the general development of programs.
You should understand that this course does not tell the whole story. First, we do not look at many more advanced programming techniques (e.g. fully object-oriented programming). These are left for the second year course on Object-Oriented Design. Third, we do not cover the team-work and project-management aspects of large-scale system building. These are addressed by the Group Projects in the second year.
PR2 is divided into the following chapters.
Building real systems in a commercial/industrial environment is very different to the kind of programming you have done so far :-
The discipline of programming in such an environment is called Software Engineering. Its main goals are to produce systems which are modular, maintainable, extendable, work properly and make maximum re-use of existing code.
The key to building such systems is ABSTRACTION. We have already seen on the CUA course that the history of programming languages is one of increasing abstraction. Abstraction allows us to form a view of a problem which considers only necessary details and hides away unnecessary nitty-gritty. Abstraction can also be used to give different people different views of the same problem. You have already come across this in PR1.
For example, imagine that I write a C++ function called power which raises one number to the power of another. I might show you the function heading with suitable comments describing what the function does. You would then know how to use it in your own program.
// raise the number n to the power p and return the answer // works for floating point n and positive and negative integer p float power(float n, int p);
Note that you don't have to know how it is implemented! In other words, you have an abstract view of the function. I then have to write a specific implementation. Here are two different ones.
// implementation using a for loop float power(float n, int p) { int i; float res = 1; // test for positive or negative power if(p >= 0) // multiply 1 by n p times for(i = 0; i < p; i++) res = res * n; else // divide 1 by n p times for(i = p; i < 0; i++) res = res / n; return res; } // implementation using a while loop float power(float n, int p) { float res = 1; // test whether positive or negative power if(p >= 0) // multiply 1 by n p times while(p--) res = res * n; else // divide 1 by n p times while(p++) res = res / n; return res; }
Note that you are unware of which implementation I use. Even more importantly, I can change the implementation - perhaps make it more efficient - and your code won't have to be changed. We call this procedural abstraction.
Put more formally, procedural abstraction is used to define an interface between the user and the implementor of a function resulting in more modular code.
Data Abstraction takes these ideas much further. Instead of defining a single function, a whole new data type is defined.
Each language provides a basic set of types (e.g. float, int, char in C++) and gives the user operations for manipulating variables of these types (e.g. + - * etc). Data Abstraction allows the definition of new types, complete with a set of operations or functions, which appear as if they were part of the language. We call these Abstract Data Types (ADTs).
The idea is that a new ADT is defined so that its users see an interface which provides an abstract view of the type and only its implementors see its internal details. The interface is defined by a set of functions which give access to the type. It is only possible to access the type through its functions.
We can show this by the following picture:
----------------- | ADT | | (e.g. rational|<--------- Interface = set of | number) |<--------- functions | |<--------- seen by user | internal | | details seen | | by implementor| -----------------
Several points need emphasising:-
Thus, ADTs will form the basis of our approach to programming in the large. Notice, that they represent a general technique, not a specific part of any particular language (although several languages provide good support for building them). In the next chapter we will see how to realise ADTs in C++ using the new mechanism of classes.
Notes converted from troff to HTML by an Eric Foxley shell script, email errors to me!