Fundamental Programming Principles I

Revision 5
18.1.2009


Introduction

Software of good quality is intended as software that responds to some specified quality criteria, such as correctness, maintainability, robustness, etc. It follows that to develop software of good quality, it is necessary to have discipline, to apply a coherent system of techniques through the whole life cycle of a program, and to apply well-known principles to all phases of software development and maintenance. In synthesis, we can say that the two fundamental programming principles are abstraction and structuration.


Principle: Abstraction

Abstraction, in the programming context, is a generalization process where the developer concentrates on similarities pertaining to a set of objects, excluding other aspects that diversify these objects, arriving in this way at the construction of a new object that generalizes them. In designing programs the developer can proceed describing initially, in abstract way, the algorithm and the data on which it operates, then proceeding to refinements of the abstract description in terms of more and more detailed and precise abstractions.

There are three types of abstractions:

  • The Functional Abstraction, is used when a set of instructions or complex operations are complessively expressed with a procedure or function.
  • The Data Type Abstraction, is used with abstract data types and their related operations, ignoring aspects pertaining the low-level data representation and the related manipulations.
  • The Control Abstraction, consists in using high-level instructions without specifying the exact mechanism of implementation.

Principle: Structuration

Structuration means subdividing programs in modules. The fundamental mechanism by which structure can be given to a program consists in using functional abstraction. Here I use the general term module to indicate both a procedure or a function. The criteria by which the set of instructions of a program can be subdivided and structured in a set of modules are functional independence, hierarchical decomposition, information restriction, and data type abstraction.

  • Functional Independence, consists in giving the maximum independence to the modules of which the program is composed. More each module executes a well-defined program feature, distinguished and independent from the features executed by other modules, more the program is complessively understandable. Functional independence can in turn be expressed in terms of two criteria: the maximization of cohesion of computations internal to each module, and the minimization of coupling between modules due to shared state (shared data between modules, global variables). Functional independence is often called "separation of concerns", it is one of the key factors that enables reusability of modules. Less frequently we see the term "separation of duty", which seem to have approximately the same conceptual meaning as "separation of concerns".
  • Hierarchical Decomposition, consists in decomposing a program in modules that realize simple features local to a particular aspect of the problem to solve. In a program decomposed into a set of modules that call themselves hierarchically, following a tree structure, the feature executed by each module is easier to understand and can be easily identified relatively to the features of other modules.
  • Information Restriction, basically signify that each module communicates with other modules by means of a restricted non-shared data flow in the form of a well-defined set of parameters, restricting in this way the visibility of the underlying mechanisms by which local computations are executed internally to each module.
  • Data Type Abstraction, by which some particular modules can be seen as representations of abstract data types. Other modules interact with these particular modules only by means of the operations defined on the represented abstract data types.

Abstraction and Structuration: A Twofold Principle

Guaranteeing the good modularity of a program, allows attainment of other properties, such as readability, maintainability, and correctness. On the basis of what said above, proceeding by levels of abstraction, and structuring a program in modules, are two different views of the same general principle:

  • On one hand, by proceeding from the abstract to the concrete, development of highly structured programs is generally guaranteed.
  • On the other hand, to guarantee good modularity of a program, the developer must have at each moment a global vision of the program, that can be obtained more easily by proceeding with structuration in successive refinements.

Design strategies

The design strategy inspired directly to the abstraction principle introduced above is called top-down strategy. Using the top-down strategy, the developer decomposes a problem iteratively into smaller subproblems, the combined solution of which constitutes the solution of the initial problem. Using the abstractions seen above, the developer proceeds in the decomposition of the problem until each single subproblem has a simple structure that can be expressed directly by instructions of the programming language.

In practice, the first decomposition of a program, can be seen as a functional abstraction, while the successive decompositions are applications of data type abstration and control abstraction.

  • The functional decomposition is used each time the developer establishes that some program feature responds to a significative modularity criteria.
  • The sequential decomposition is used when in the analysis of a problem the developer sees that the problem can be decomposed in a set (or serie) of elementar actions that must be executed one after the other.
  • The alternative decomposition is used when the problem can be decomposed in terms of choices between different alternatives.
  • The iterative decomposition is used when the developer sees a refinement of the problem where some actions must be executed many times.

The inverse strategy, called bottom-up strategy, is used sometimes to integrate preexisting modules into a program, or to integrate a small program into a bigger one.


Code properties

Code has a number of properties. In no particular order:

  • Correctness, intended as adhesion of the program's implementation to the problem specification. Correctness of a program should be verifiable, so correctness leads us to the next fundamental property: verifiability.
  • Verifiability, it should be reasonably easy to verify the correctness of a program.

    Locality - the verifier shouldn't have to refer to many separate pieces of code to prove correctness. It occurs that locality is connected to functional independence as it promotes cohesion of code.

  • Robustness, intended as capacity of the program to operate predictably, despite the introduction of input data incoherent with its specification.
  • Maintainability, intended as ease of adaptation and transformation of the program when the specification changes. A program that is easy to modify shouldn't be fragile. A small modification shouldn't make it fail in unexpected ways.

    It occurs that the robustness and maintainability properties are somehow connected.

  • Simplicity, intended as the possibility to identify easily the program starting from the corresponding problem.
  • Efficiency, intended as the capacity of the program to execute with small use of resources (CPU, RAM, stack/heap, disk, etc.). An additional requirement for real-time programs is to execute within bound space and time.
  • Modularity, intended as the degree of internal organization and structuration of the program, that must allow the ability to understand how the various parts of the program cooperate to the general scope.
  • Readability, intended as the ability, of the designer or other persons, to understand the program at each moment. For a program to be easy to read and understand, it should not be obscured by non-essential coding forms. It should be easy for the person reading it to assimilate one concept at a time.
  • Portability, intended as the possibility to execute the program in various computing environments without modification.
  • Generality, intended as the degree of generality that the program provides in the resolution not only of a single problem, but rather of a class of problems, so that it is economically advantageous.

Revalidate HTML