2 Object-Oriented Programming Languages. Evolutions, Comparisons and Examples
Programming languages are the first "environments" in which the new software concepts and paradigms were tested and disseminated. The history was also repeated in the case of "object-oriented programming". For more then a decade, OOLs have retained the attention of the software community. The number of pure or hybrid OOLs is greater compared to the other language types. This thesis chapter briefly describes the events that have marked the evolution of OOL, it describes some criteria which should be considered when comparing OOL with classes, criteria which exemplify the solutions proposed by two "model" languages, Smalltalk and Eiffel, mentioning in the last part some particularities of the Java language. In this abstract, only the comparison criteria and the conclusions are presented.
2.1 Object-Oriented Language classification
When they decide for an object-oriented style for software development, most developers take into consideration only some programming languages. In most cases this decision is not due to the technical qualities of potential candidates but to some pragmatic aspects promoted by development tools (implemented libraries, graphical interfaces, etc.), and to the availability of experienced specialists in this field.
It seems that the largest classification of OOLs is presented in [Massini90]. He classifies languages by their point of view on the object concepts in class languages, frame languages and actor languages:
Object-oriented languages are those languages that combine state transitions, communication and classification.
Object-oriented languages are the languages based upon objects, where objects which share the same behavior are grouped in classes and classes are classified by the generalization/specialization relation.
Languages which are limited to ensuring the linguistic support necessary for implementing and using objects are called object-based languages.
Abstraction and decomposition are used to control the complexity of the application and the impact of change. Taking this into consideration, the analysis of the way in which class languages support abstraction and decomposition is worth studying.
2.2 An OOL comparison from the kind of abstractions point of view
2.2.1 Function abstraction or procedural abstraction
The first high-level language, FORTRAN, has also been the language that has introduced the first abstraction and decomposition mechanism, subroutines and functions. The impact of the changes in a function's implementation is minimized because clients use only an external abstraction. The terms: function abstraction, procedural abstraction or parameterized abstraction are used to refer to this abstraction.
2.2.2 Data abstraction
Functions extend the basic language by adding new operations. For languages that support only function abstraction, the type of the function's parameters is a predefined one. Programmers have felt that, apart from extending the operations, type extension is at least as necessary. This is achieved by introducing some user-defined types, characterized by behavior. The structure of the new types as well as behavior implementation are hidden. The user of such a type can communicate with it only by the interface formed by the functions signature. These data types are called abstract data types as their instances are described exclusively by their properties. Hiding function implementation is inherited from procedural abstraction. Data abstraction has been implemented by cluster in CLU, by module in Modula-2, by package in Ada, and by class in OOL.
2.2.3 Type abstraction
Even if the term of type abstraction is not as widely used as data abstraction, it expresses the essence of object-oriented languages. The most often used term, inheritance, concentrates upon the secondary effects of reuse.
The real power and the elegance of OOL is in their ability to build software systems based on generalized types, associated with the possibility of substituting the instances of generalized types with instances of some specialized types, including instances whose the type was not known when building the application (program).
2.2.4 Differences in function abstraction
Even if subject to controversy, the most important difference between object-oriented languages with classes can be found in function differences.
In OOL, function abstraction becomes secondary, type abstraction and data abstraction moving in the foreground. Most OOL emphasize this, requiring functions to be members of a type (Eiffel, Java, Smalltalk). Normally, functions have special access rights when implementing types, the main advantage being conceptual simplicity.
OOL built on the structure of traditional languages allow the existence of type-independent functions. C++ allows global functions. The languages that offer a strong support for modularization (Oberon-2 and Ada95) do not request that the functions in a package be associated to a type. The main disadvantage is that, in this case, nothing stops the programmer to use traditional design approaches. There is an advantage though: the simplification of the interface with the old code.
2.2.5 Differences in data abstraction
Data abstraction is the most important concept of the OOL. There are important differences between the ways in which this abstraction is made.
All OOL support data abstraction by limiting the access to type implementation. Normally, this is achieved by defining some features that are accessible by any function, and other members that can be accessed only by functions belonging to that type. Concerning this problem, C++ has a flexible approach. Member functions and member data can be declared.
C++ also offers a supplementary possibility - functions that are not type members can be declared friends, which gives them the same rights as member functions.
The Smalltalk model is simpler. All data members are the equivalents of protected and all the function members are public. Eiffel uses an export clause which can exactly declare what other types have access to a certain characteristic.
Allowing a specialized access to type implementation is often convenient and efficient. Still, it violates the principle of information hiding.
2.2.6 Differences in type abstraction
There are significant differences even in the definition of the OOL features.
Ada95, Oberon-2, Smalltalk and Java allow the specialization of only one type. A natural extension would be to allow a type to directly specialize more types. C++, CLOS and Eiffel allow multiple inheritance.
Java has a more complex system. It allows the specialization of only one class, but it supports this specialization by many interfaces. When defining types and their specialization, most languages combine specification with implementation. A separation can be simulated with the help of C++ 's abstract classes or of the deferred classes in Eiffel, even if these allow the specification of implementation.
C++ refines inheritance with the help of three categories of specialization: public, protected and private, and Eiffel allows repeat inheritance.
2.3 Other criteria that can be taken into consideration in the OOLs comparison
2.3.1 Object accessing
Is another feature allowing languages differentiation. Smalltalk , Eiffel and Java (purely object-oriented languages) allow only indirect objects reference by the equivalent of pointers. Oberon-2, Ada95 and especially C++ also allow direct manipulation.
If indirect reference only is allowed, memory management is simplified. But often, direct access to objects is more efficient and defines more clearly the system's semantics. In order to distinguish between reference and object semantics, Smalltalk uses the concepts of deep and shallow copy. In C++ this is not a problem, as here the desired semantics can be expressed directly.
2.3.2 Language uniformity
Smalltalk and CLOS have a simple and elegant model in which everything is an object, including types. At least conceptually, types can be created and modified at execution, offering enormous power and flexibility.
2.3.3 Type checking
The typing problem is not a simple one, especially if we desire to build flexible and reliable systems. To cite Meyer, in OOL the typing problem is "easy to state but not to solve" [Meyer97]
When executing a callx.f(arg) (where x is attached to an instance of type T), there is a type error if:
The typing problem can be stated: "When do we need to know if at execution a type violation can appear?"
As Meyer says, the reasonable answer is "As soon as possible !" Completely eliminating type errors at execution is not a realistic objective - at least for the time being !
Attempts to solve these problems emphasized two extreme approaches:
For these approaches, the terms untyped (Smalltalk, CLOS, etc.), and typed (C++, Eiffel, Java, Ada, etc.) are used respectively.
An OOL is static typed if it is equipped with a set of consistency rules, required by compilers, rules whose checking in a program code guarantees that at execution there will not be a type error.
The term "strong typed" found in literature corresponds to the nature of this definition of "all or nothing", that requires rules which can guarantee the absence of type violations. Typing weak forms are possible whose rules eliminate some type violations, but not all. The goal pursued in flexible languages (like Eiffel for example) is to obtain the strongest possible form.
In most OOLs, all member functions are polymorphic. C++ is much more flexible, allowing the individual declaration of polymorphic functions and requiring that some functions are not polymorphic (constructors for example). Smalltalk has a similar mechanism too, implemented for the methods whose selector starts with "basic". Although more complicated, this flexibility has the advantage of efficiency and prevents these functions from being overwritten.
2.3.5 Parameterized types
Many strong typed OOLs support the concept of parameterized types (generic or "template"). Parameterization offers for types abstraction possibilities similar to those provided by inheritance and polymorphism. One difference is that type parameterization is a compilation concept, while inheritance and polymorphism are execution concepts. The two can be combined to allow parameterized types inheritance (Eiffel).
2.3.6 Memory management
When can an object's memory be purged and who is responsible for this ? Like in the case of traditional languages, object-oriented languages are divided in two groups. Most of them have evolved towards "garbage collection". All pure OOLs have garbage collectors.
The big gain in the case of garbage collectors use is the exclusion of a great number of errors. The compromise can be the systems efficiency and utility, especially of those treating real time control. Some languages, for example Eiffel, allow for some code portions, the deactivation of garbage collection. The resource management technique, implemented in C++ through constructors and destructors is an elegant solution.
As we have pointed out with other occasions, OOLs cannot be appreciated separated from the environments in which they are incorporated. Besides the environment quality there is another aspect of interest, the solution chosen for the object model. Smalltalk, Eiffel, Java, C++ offer interesting solutions, which are still sources of inspiration.
Generally, the decision of choosing an object-oriented language is a compromise among flexibility, safety and efficiency. OOL are more than just a collection of features. Conceptual integrity, simplicity and solutions offered for modeling are the language's true measure. The answer to the question "Is there a best OOL ?" is: "Yes, there is a language best suited for certain software systems, but there is no language best for all systems". If we think about the fact that an object-oriented language is more than a simple tool, we realize that this answer is a reasonable one.
We would like to emphasize again the particular importance of development environments in which OOL are practically "immersed". This is due to the fact that one of the fundamental goals pursued by object-oriented programming is reusability. When a suitable environment is missing, reuse remains a simple statement. However, we must not forget the language's qualities, without which we cannot obtain reliable components, as Bertrand Meyer reminds us.
Apart from their other merits OOL have another one we have not emphasized until now. By focusing the attention on the early software development stages, they have created favorable conditions for the apparition of "visual modeling languages" , that will be presented in chapter 4.